Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 53 additions & 46 deletions src/lib/libsyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,57 +108,70 @@ var SyscallsLibrary = {
},

$parseSelectFDSet__internal: true,
$parseSelectFDSet: (readfds, writefds, exceptfds) => {
var total = 0;
$parseSelectFDSet: (readfds, writefds, exceptfds, nfds) => {
const FD_ISSET = (set, fd) => set[(fd / 32) >>> 0] & (1 << (fd & 31));
const FD_CLR = (set, fd) => set[(fd / 32) >>> 0] &= ~(1 << (fd & 31));
const FD_SET = (set, fd) => set[(fd / 32) >>> 0] |= (1 << (fd & 31));
const numWords = ((nfds + 31) / 32) >>> 0;

var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0),
srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0);
var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0),
srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0);
var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0),
srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
#if ASSERTIONS
const FD_SETWORDS = {{{ cDefs.FD_SETSIZE }}} / 32;
assert(numWords <= FD_SETWORDS);
#endif

var dstReadLow = 0,
dstReadHigh = 0;
var dstWriteLow = 0,
dstWriteHigh = 0;
var dstExceptLow = 0,
dstExceptHigh = 0;
const srcMerged = new Array(numWords);
const srcRead = new Array(numWords).fill(0);
const srcWrite = new Array(numWords).fill(0);
const srcExcept = new Array(numWords).fill(0);
const dstRead = new Array(numWords).fill(0);
const dstWrite = new Array(numWords).fill(0);
const dstExcept = new Array(numWords).fill(0);

var check = (fd, low, high, val) => fd < 32 ? (low & val) : (high & val);
let total = 0;

for (let i = 0; i < numWords; i++) {
if (readfds) {
srcRead[i] = {{{ makeGetValue('readfds', 'i * 4', 'i32') }}};
}
if (writefds) {
srcWrite[i] = {{{ makeGetValue('writefds', 'i * 4', 'i32') }}};
}
if (exceptfds) {
srcExcept[i] = {{{ makeGetValue('exceptfds', 'i * 4', 'i32') }}};
}
srcMerged[i] = srcRead[i] | srcWrite[i] | srcExcept[i];
}

return {
allLow: srcReadLow | srcWriteLow | srcExceptLow,
allHigh: srcReadHigh | srcWriteHigh | srcExceptHigh,
getTotal: () => total,
check: (fd) => {
return FD_ISSET(srcMerged, fd);
},
setFlags: (fd, flags) => {
var mask = 1 << (fd % 32);

if ((flags & {{{ cDefs.POLLIN }}}) && check(fd, srcReadLow, srcReadHigh, mask)) {
fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask);
if ((flags & {{{ cDefs.POLLIN }}}) && FD_ISSET(srcRead, fd)) {
FD_SET(dstRead, fd);
total++;
}
if ((flags & {{{ cDefs.POLLOUT }}}) && check(fd, srcWriteLow, srcWriteHigh, mask)) {
fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask);
if ((flags & {{{ cDefs.POLLOUT }}}) && FD_ISSET(srcWrite, fd)) {
FD_SET(dstWrite, fd);
total++;
}
if ((flags & {{{ cDefs.POLLPRI }}}) && check(fd, srcExceptLow, srcExceptHigh, mask)) {
fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask);
if ((flags & {{{ cDefs.POLLPRI }}}) && FD_ISSET(srcExcept, fd)) {
FD_SET(dstExcept, fd);
total++;
}
},
commit: () => {
if (readfds) {
{{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}};
{{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}};
}
if (writefds) {
{{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}};
{{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}};
}
if (exceptfds) {
{{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}};
{{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}};
for (let i = 0; i < numWords; i++) {
if (readfds) {
{{{ makeSetValue('readfds', 'i * 4', 'dstRead[i]', 'i32') }}};
}
if (writefds) {
{{{ makeSetValue('writefds', 'i * 4', 'dstWrite[i]', 'i32') }}};
}
if (exceptfds) {
{{{ makeSetValue('exceptfds', 'i * 4', 'dstExcept[i]', 'i32') }}};
}
}
}
};
Expand Down Expand Up @@ -405,7 +418,7 @@ var SyscallsLibrary = {
__syscall_socket: (domain, type, protocol) => {
var sock = SOCKFS.createSocket(domain, type, protocol);
#if ASSERTIONS
assert(sock.stream.fd < 64); // XXX ? select() assumes socket fd values are in 0..63
assert(sock.stream.fd < {{{ cDefs.FD_SETSIZE }}}); // XXX ? select() assumes socket fd values are in 0..63
#endif
return sock.stream.fd;
},
Expand Down Expand Up @@ -617,18 +630,13 @@ var SyscallsLibrary = {
// timeout is supported, although on SOCKFS these are ignored and always treated as 0 - fully async
// and PIPEFS supports timeout only when the select is called from a worker.
#if ASSERTIONS
assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits // TODO: this could be 1024 based on current musl headers
assert(nfds <= {{{ cDefs.FD_SETSIZE }}}, 'nfds must be less than or equal to FD_SETSIZE');
#if PTHREADS
assert(!ENVIRONMENT_IS_PTHREAD, '_newselect_js must be called in the main thread');
#endif
#endif

var fdSet = parseSelectFDSet(readfds, writefds, exceptfds);

var allLow = fdSet.allLow;
var allHigh = fdSet.allHigh;

var check = (fd, low, high, val) => fd < 32 ? (low & val) : (high & val);
var fdSet = parseSelectFDSet(readfds, writefds, exceptfds, nfds);

#if PTHREADS
var makeNotifyCallback = null;
Expand Down Expand Up @@ -661,8 +669,7 @@ var SyscallsLibrary = {
#endif

for (var fd = 0; fd < nfds; fd++) {
var mask = 1 << (fd % 32);
if (!(check(fd, allLow, allHigh, mask))) {
if (!fdSet.check(fd)) {
continue; // index isn't in the set
}

Expand Down
6 changes: 6 additions & 0 deletions src/struct_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,12 @@
"SIGTERM"
]
},
{
"file": "sys/select.h",
"defines": [
"FD_SETSIZE"
]
},
{
"file": "sys/socket.h",
"defines": [
Expand Down
1 change: 1 addition & 0 deletions src/struct_info_generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
"EWOULDBLOCK": 6,
"EXDEV": 75,
"EXFULL": 115,
"FD_SETSIZE": 1024,
"FIONBIO": 21537,
"FIONREAD": 21531,
"F_DUPFD": 0,
Expand Down
1 change: 1 addition & 0 deletions src/struct_info_generated_wasm64.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
"EWOULDBLOCK": 6,
"EXDEV": 75,
"EXFULL": 115,
"FD_SETSIZE": 1024,
"FIONBIO": 21537,
"FIONREAD": 21531,
"F_DUPFD": 0,
Expand Down
2 changes: 1 addition & 1 deletion test/sockets/test_sockets_echo_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void main_loop() {
FD_ZERO(&fdw);
FD_SET(server.fd, &fdr);
FD_SET(server.fd, &fdw);
res = select(64, &fdr, &fdw, NULL, &tv);
res = select(FD_SETSIZE, &fdr, &fdw, NULL, &tv);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this is not how select is supposed to work. This should be server.fd + 1 I think,

       nfds   This argument should be set to the highest-numbered file descriptor in any of the three sets, plus 1.  The indicated file descriptors in each set are checked, up
              to this limit (but see BUGS).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be up for making that change instead as a separate PR?

Copy link
Collaborator Author

@inolen inolen Dec 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbc100 yah, I would expect maxfd + 1 to be passed, but I guess you could technically pass anything up to the max size providing the sets are zero'd out.

I just saw the 64 and changed it to match.

if (res == -1) {
perror("select failed");
finish(EXIT_FAILURE);
Expand Down
2 changes: 1 addition & 1 deletion test/sockets/test_sockets_echo_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void main_loop() {
if (client.fd) FD_SET(client.fd, &fdr);
if (client.fd) FD_SET(client.fd, &fdw);
#endif
res = select(64, &fdr, &fdw, NULL, &tv);
res = select(FD_SETSIZE, &fdr, &fdw, NULL, &tv);
if (res == -1) {
perror("select failed");
exit(EXIT_SUCCESS);
Expand Down
2 changes: 1 addition & 1 deletion test/sockets/test_sockets_partial_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void iter() {
// make sure that sockfd is ready to read
FD_ZERO(&fdr);
FD_SET(sockfd, &fdr);
res = select(64, &fdr, NULL, NULL, NULL);
res = select(FD_SETSIZE, &fdr, NULL, NULL, NULL);
if (res == -1) {
perror("select failed");
finish(EXIT_FAILURE);
Expand Down
2 changes: 1 addition & 1 deletion test/sockets/test_sockets_partial_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ void iter() {
FD_ZERO(&fdw);
FD_SET(serverfd, &fdr);
if (clientfd) FD_SET(clientfd, &fdw);
res = select(64, &fdr, &fdw, NULL, NULL);
res = select(FD_SETSIZE, &fdr, &fdw, NULL, NULL);
if (res == -1) {
perror("select failed");
exit(EXIT_SUCCESS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ void main_loop() {
// select should tell us 0 handles are ready
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, &sett, NULL, NULL, NULL);
selectRes = select(FD_SETSIZE, &sett, NULL, NULL, NULL);
if (selectRes != 0) {
printf("case 0: read select != 0 (%d)\n", selectRes);
finish(EXIT_FAILURE);
Expand All @@ -63,7 +63,7 @@ void main_loop() {
// the connection either is setting up or is established and writing is possible
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, NULL, &sett, NULL, NULL);
selectRes = select(FD_SETSIZE, NULL, &sett, NULL, NULL);
if (selectRes == -1) {
printf("case 0: write select == -1\n");
finish(EXIT_FAILURE);
Expand All @@ -86,7 +86,7 @@ void main_loop() {
// has sent the data and then closed the connection
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, &sett, NULL, NULL, NULL);
selectRes = select(FD_SETSIZE, &sett, NULL, NULL, NULL);
if (selectRes == -1) {
printf("case 1: read selectRes == -1\n");
finish(EXIT_FAILURE);
Expand Down Expand Up @@ -114,7 +114,7 @@ void main_loop() {
// succeed, but the socket should not set in the set.
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, NULL, &sett, NULL, NULL);
selectRes = select(FD_SETSIZE, NULL, &sett, NULL, NULL);
if (selectRes != 0 || FD_ISSET(sockfd, &sett)) {
printf("case 2: write selectRes != 0 || FD_ISSET(sockfd, &sett)\n");
finish(EXIT_FAILURE);
Expand All @@ -124,7 +124,7 @@ void main_loop() {
// has to succeed because there is still data in the inQueue
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, &sett, NULL, NULL, NULL);
selectRes = select(FD_SETSIZE, &sett, NULL, NULL, NULL);
if (selectRes != 1) {
printf("case 2: read selectRes != 1\n");
finish(EXIT_FAILURE);
Expand Down Expand Up @@ -152,7 +152,7 @@ void main_loop() {
// should succeed
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, &sett, NULL, NULL, NULL);
selectRes = select(FD_SETSIZE, &sett, NULL, NULL, NULL);
if (selectRes != 1) {
printf("case 3: read selectRes != 1\n");
finish(EXIT_FAILURE);
Expand Down
Loading