Skip to content
Open
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
6 changes: 5 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ See docs/process.md for more on how version tagging works.
out to `wasm-bindgen` in the users's path and integrate the wasm-bindgen JS
with the normal Emscripten JS. Some wasm-bindgen features may not yet be fully
supported. (#23493)
- The select() and poll() syscalls now fail with EINTR when no FDs are active
and they are asked to block in a build that does not support blocking (i.e.
now JSPI or ASYNCIFY). Previously they would return 0 but without any result
FSs set (i.e. as if the timeout at expired). (#27049)

6.0.0 - 06/04/26
----------------
Expand Down Expand Up @@ -84,7 +88,7 @@ See docs/process.md for more on how version tagging works.
5.0.7 - 04/30/26
----------------
- mimalloc was updated to 3.3.1. (#26696)
- The `WASM_JS_TYPES` setting was removed, as the corresponsing propsal was
- The `WASM_JS_TYPES` setting was removed, as the corresponding proposal was
pushed back to phase 1. (#26739)
- The `-sDETERMINISTIC` setting was removed. This setting just injected
`src/deterministic.js` as a `--pre-js`. For now, this file remains part of
Expand Down
8 changes: 7 additions & 1 deletion src/lib/libsyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,9 +634,15 @@ var SyscallsLibrary = {
#endif

var count = doPoll(fds, nfds, 0, undefined);
if (!count && timeout) {
// We cannot actually block here since we are not in an async context,
// so return -EINTR, as if we were inturrupted by a signal.
#if ASSERTIONS
if (!count && timeout != 0) warnOnce('non-zero poll() timeout not supported: ' + timeout)
warnOnce('non-zero poll() timeout not supported: ' + timeout)

#endif
return -{{{ cDefs.EINTR }}};
}
return count;
},
// The shared readiness derivation: one pass over the pollfds, writing
Expand Down
4 changes: 2 additions & 2 deletions system/lib/libc/musl/src/select/select.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ int select(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict

if (s<0 || us<0) return __syscall_ret(-EINVAL);
#ifdef __EMSCRIPTEN__
return emscripten_select(n, rfds, wfds, efds, tv);
return __syscall_ret(emscripten_select(n, rfds, wfds, efds, tv));
#else
if (us/1000000 > max_time - s) {
s = max_time;
Expand Down Expand Up @@ -85,7 +85,7 @@ static int emscripten_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set
int rtn = __syscall_poll((intptr_t)fds, n, timeout);
if (rtn < 0) {
free(fds);
return -1;
return rtn;
}

// Part 2: Translate the result of poll into the results of select();
Expand Down
9 changes: 6 additions & 3 deletions system/lib/wasmfs/syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1391,7 +1391,7 @@ int __syscall_poll(intptr_t fds_, int nfds, int timeout) {

// Process the list of FDs and compute their revents masks. Count the number
// of nonzero such masks, which is our return value.
int nonzero = 0;
int count = 0;
for (nfds_t i = 0; i < nfds; i++) {
auto* pollfd = &fds[i];
auto fd = pollfd->fd;
Expand Down Expand Up @@ -1426,14 +1426,17 @@ int __syscall_poll(intptr_t fds_, int nfds, int timeout) {
// TODO: set the state based on the state of the other end of the pipe, for
// pipes (POLLERR | POLLHUP)
if (mask) {
nonzero++;
count++;
}
pollfd->revents = mask;
}
// TODO: This should block based on the timeout. The old FS did not do so due
// to web limitations, which we should perhaps revisit (especially with
// pthreads and asyncify).
return nonzero;
if (timeout && !count) {
return -EINTR;
}
return count;
}

// libc routes zero-timeout poll() calls here (see musl's poll.c). WasmFS's
Expand Down
6 changes: 3 additions & 3 deletions test/codesize/test_codesize_hello_dylink_all.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a.out.js": 268274,
"a.out.nodebug.wasm": 587640,
"total": 855914,
"a.out.js": 268293,
"a.out.nodebug.wasm": 587659,
"total": 855952,
"sent": [
"IMG_Init",
"IMG_Load",
Expand Down
38 changes: 38 additions & 0 deletions test/core/test_pipe_select.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#ifdef __EMSCRIPTEN__
#include <emscripten/threading.h>
#endif

int pipe_a[2];

Expand Down Expand Up @@ -35,6 +39,40 @@ int main() {
assert(get_available(pipe_a[0]) == strlen(t));
assert(get_available(pipe_a[1]) == strlen(t));



// Test select with timeout when no FDs are ready
{
int pipe_b[2];
assert(pipe(pipe_b) == 0);

fd_set fds;
FD_ZERO(&fds);
FD_SET(pipe_b[0], &fds);
struct timeval tv = {0, 10000}; // 10ms

#ifdef __EMSCRIPTEN__
if (emscripten_is_main_runtime_thread()) {
printf("Main thread: expecting EINTR\n");
int res = select(pipe_b[0] + 1, &fds, NULL, NULL, &tv);
assert(res == -1);
assert(errno == EINTR);
} else {
printf("Worker thread: expecting timeout\n");
int res = select(pipe_b[0] + 1, &fds, NULL, NULL, &tv);
assert(res == 0);
}
#else
// Native: should timeout
printf("Native: expecting timeout\n");
int res = select(pipe_b[0] + 1, &fds, NULL, NULL, &tv);
assert(res == 0);
#endif

close(pipe_b[0]);
close(pipe_b[1]);
}

close(pipe_a[0]);
close(pipe_a[1]);
return 0;
Expand Down
2 changes: 1 addition & 1 deletion test/sockets/sdl2_net_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void main_loop() {

/* Check the sd if there is a pending connection.
* If there is one, accept that, and open a new socket for communicating */
SDLNet_CheckSockets(state.socketSet, 20);
SDLNet_CheckSockets(state.socketSet, 0);
int serverSocketActivity = SDLNet_SocketReady(state.sd);

if (serverSocketActivity)
Expand Down
11 changes: 8 additions & 3 deletions test/sockets/test_sockets_partial_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ void iter() {
FD_SET(sockfd, &fdr);
res = select(64, &fdr, NULL, NULL, NULL);
if (res == -1) {
perror("select failed");
finish(EXIT_FAILURE);
} else if (!FD_ISSET(sockfd, &fdr)) {
if (errno != EINTR) {
perror("select failed");
finish(EXIT_FAILURE);
}
return;
}

if (!FD_ISSET(sockfd, &fdr)) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions test/sockets/test_sockets_partial_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ void iter() {
if (clientfd) FD_SET(clientfd, &fdw);
res = select(64, &fdr, &fdw, NULL, NULL);
if (res == -1) {
if (errno == EINTR) {
return;
}
perror("select failed");
exit(EXIT_SUCCESS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ void main_loop() {
int selectRes;
ssize_t transferAmount;
fd_set sett;
struct timeval zero_timeout = {0, 0};

switch (state) {
case 0:
Expand All @@ -53,7 +54,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(64, &sett, NULL, NULL, &zero_timeout);
if (selectRes != 0) {
printf("case 0: read select != 0 (%d)\n", selectRes);
finish(EXIT_FAILURE);
Expand All @@ -63,7 +64,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(64, NULL, &sett, NULL, &zero_timeout);
if (selectRes == -1) {
printf("case 0: write select == -1\n");
finish(EXIT_FAILURE);
Expand All @@ -86,7 +87,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(64, &sett, NULL, NULL, &zero_timeout);
if (selectRes == -1) {
printf("case 1: read selectRes == -1\n");
finish(EXIT_FAILURE);
Expand Down Expand Up @@ -114,7 +115,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(64, NULL, &sett, NULL, &zero_timeout);
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 +125,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(64, &sett, NULL, NULL, &zero_timeout);
if (selectRes != 1) {
printf("case 2: read selectRes != 1\n");
finish(EXIT_FAILURE);
Expand Down Expand Up @@ -152,7 +153,7 @@ void main_loop() {
// should succeed
FD_ZERO(&sett);
FD_SET(sockfd, &sett);
selectRes = select(64, &sett, NULL, NULL, NULL);
selectRes = select(64, &sett, NULL, NULL, &zero_timeout);
if (selectRes != 1) {
printf("case 3: read selectRes != 1\n");
finish(EXIT_FAILURE);
Expand Down
2 changes: 1 addition & 1 deletion test/unistd/misc.out
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ link: -1, errno: 34
lockf(good): 0, errno: 0
lockf(bad): -1, errno: 8
nice: -1, errno: 63
pause: 0, errno: 0
pause: -1, errno: 27
pipe(good): 0, errno: 0
pipe(bad): -1, errno: 21
pipe2(good): 0, errno: 0
Expand Down
Loading