Skip to content

Commit d44829d

Browse files
committed
Implement only poll() and not select() in JS
This change improves the implementation of poll() to support blocking when called from threads (see #25523) and moves the select-based-on-poll implemenation from being wasmfs specific to be always being used.
1 parent 8c5cdc8 commit d44829d

File tree

16 files changed

+400
-323
lines changed

16 files changed

+400
-323
lines changed

src/lib/libsigs.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ sigs = {
262262
__syscall_newfstatat__sig: 'iippi',
263263
__syscall_openat__sig: 'iipip',
264264
__syscall_pipe__sig: 'ip',
265-
__syscall_poll__sig: 'ipii',
266265
__syscall_readlinkat__sig: 'iippp',
267266
__syscall_recvfrom__sig: 'iippipp',
268267
__syscall_recvmsg__sig: 'iipiiii',
@@ -380,7 +379,7 @@ sigs = {
380379
_mmap_js__sig: 'ipiiijpp',
381380
_msync_js__sig: 'ippiiij',
382381
_munmap_js__sig: 'ippiiij',
383-
_newselect_js__sig: 'ippipppj',
382+
_poll_js__sig: 'ipiipp',
384383
_setitimer_js__sig: 'iid',
385384
_timegm_js__sig: 'jp',
386385
_tzset_js__sig: 'vpppp',

src/lib/libsyscall.js

Lines changed: 69 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
* SPDX-License-Identifier: MIT
55
*/
66

7-
{{{
8-
DEFAULT_POLLMASK = cDefs.POLLIN | cDefs.POLLOUT;
9-
}}}
10-
117
var SyscallsLibrary = {
128
$SYSCALLS__deps: [
139
#if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM
@@ -107,63 +103,6 @@ var SyscallsLibrary = {
107103
},
108104
},
109105

110-
$parseSelectFDSet__internal: true,
111-
$parseSelectFDSet: (readfds, writefds, exceptfds) => {
112-
var total = 0;
113-
114-
var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0),
115-
srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0);
116-
var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0),
117-
srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0);
118-
var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0),
119-
srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
120-
121-
var dstReadLow = 0,
122-
dstReadHigh = 0;
123-
var dstWriteLow = 0,
124-
dstWriteHigh = 0;
125-
var dstExceptLow = 0,
126-
dstExceptHigh = 0;
127-
128-
var check = (fd, low, high, val) => fd < 32 ? (low & val) : (high & val);
129-
130-
return {
131-
allLow: srcReadLow | srcWriteLow | srcExceptLow,
132-
allHigh: srcReadHigh | srcWriteHigh | srcExceptHigh,
133-
getTotal: () => total,
134-
setFlags: (fd, flags) => {
135-
var mask = 1 << (fd % 32);
136-
137-
if ((flags & {{{ cDefs.POLLIN }}}) && check(fd, srcReadLow, srcReadHigh, mask)) {
138-
fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask);
139-
total++;
140-
}
141-
if ((flags & {{{ cDefs.POLLOUT }}}) && check(fd, srcWriteLow, srcWriteHigh, mask)) {
142-
fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask);
143-
total++;
144-
}
145-
if ((flags & {{{ cDefs.POLLPRI }}}) && check(fd, srcExceptLow, srcExceptHigh, mask)) {
146-
fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask);
147-
total++;
148-
}
149-
},
150-
commit: () => {
151-
if (readfds) {
152-
{{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}};
153-
{{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}};
154-
}
155-
if (writefds) {
156-
{{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}};
157-
{{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}};
158-
}
159-
if (exceptfds) {
160-
{{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}};
161-
{{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}};
162-
}
163-
}
164-
};
165-
},
166-
167106
$syscallGetVarargI__internal: true,
168107
$syscallGetVarargI: () => {
169108
#if ASSERTIONS
@@ -602,133 +541,106 @@ var SyscallsLibrary = {
602541
FS.chdir(stream.path);
603542
return 0;
604543
},
605-
_newselect_js__i53abi: true,
606-
_newselect_js__proxy: 'none',
607-
_newselect_js__deps: ['$parseSelectFDSet',
544+
_msync_js__i53abi: true,
545+
_msync_js: (addr, len, prot, flags, fd, offset) => {
546+
if (isNaN(offset)) return -{{{ cDefs.EOVERFLOW }}};
547+
SYSCALLS.doMsync(addr, SYSCALLS.getStreamFromFD(fd), len, flags, offset);
548+
return 0;
549+
},
550+
__syscall_fdatasync: (fd) => {
551+
var stream = SYSCALLS.getStreamFromFD(fd);
552+
return 0; // we can't do anything synchronously; the in-memory FS is already synced to
553+
},
554+
_poll_js__proxy: 'none',
555+
_poll_js__deps: [
608556
#if PTHREADS
609-
'_emscripten_proxy_newselect_finish',
557+
'_emscripten_proxy_poll_finish',
610558
#endif
611559
],
612-
_newselect_js: (ctx, arg, nfds, readfds, writefds, exceptfds, timeoutInMillis) => {
613-
// readfds are supported,
614-
// writefds checks socket open status
615-
// exceptfds are supported, although on web, such exceptional conditions never arise in web sockets
616-
// and so the exceptfds list will always return empty.
617-
// timeout is supported, although on SOCKFS these are ignored and always treated as 0 - fully async
618-
// and PIPEFS supports timeout only when the select is called from a worker.
619-
#if ASSERTIONS
620-
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
621-
#if PTHREADS
622-
assert(!ENVIRONMENT_IS_PTHREAD, '_newselect_js must be called in the main thread');
623-
#endif
624-
#endif
625-
626-
var fdSet = parseSelectFDSet(readfds, writefds, exceptfds);
627-
628-
var allLow = fdSet.allLow;
629-
var allHigh = fdSet.allHigh;
630-
631-
var check = (fd, low, high, val) => fd < 32 ? (low & val) : (high & val);
560+
_poll_js: (fds, nfds, timeout, ctx, arg) => {
561+
var count = 0;
632562
633563
#if PTHREADS
634-
var makeNotifyCallback = null;
635564
if (ctx) {
636-
// Enable event handlers only when the select call is proxied from a worker.
565+
#if RUNTIME_DEBUG
566+
dbg('async poll start');
567+
#endif
568+
// Enable event handlers only when the poll call is proxied from a worker.
637569
var cleanupFuncs = [];
638570
var notifyDone = false;
639-
makeNotifyCallback = (fd) => {
640-
var cb = (flags) => {
641-
if (notifyDone) {
642-
return;
643-
}
644-
if (fd >= 0) {
645-
fdSet.setFlags(fd, flags);
646-
}
647-
notifyDone = true;
648-
cleanupFuncs.forEach(cb => cb());
649-
fdSet.commit();
650-
__emscripten_proxy_newselect_finish(ctx, arg, fdSet.getTotal());
651-
}
652-
cb.registerCleanupFunc = (f) => {
653-
if (f != null) cleanupFuncs.push(f);
654-
}
655-
return cb;
656-
}
657-
if (timeoutInMillis > 0) {
658-
setTimeout(() => makeNotifyCallback(-1)(0), timeoutInMillis);
659-
}
660-
}
571+
function asyncPollComplete(count) {
572+
if (notifyDone) {
573+
return;
574+
}
575+
notifyDone = true;
576+
#if RUNTIME_DEBUG
577+
dbg('asyncPollComplete', count);
661578
#endif
662-
663-
for (var fd = 0; fd < nfds; fd++) {
664-
var mask = 1 << (fd % 32);
665-
if (!(check(fd, allLow, allHigh, mask))) {
666-
continue; // index isn't in the set
579+
cleanupFuncs.forEach(cb => cb());
580+
__emscripten_proxy_poll_finish(ctx, arg, count);
667581
}
668-
669-
var stream = SYSCALLS.getStreamFromFD(fd);
670-
671-
var flags = {{{ DEFAULT_POLLMASK }}};
672-
673-
if (stream.stream_ops.poll) {
674-
flags = (() => {
675-
#if PTHREADS
676-
if (makeNotifyCallback != null) {
677-
return stream.stream_ops.poll(stream, timeoutInMillis, timeoutInMillis != 0 ? makeNotifyCallback(fd) : null);
582+
function makeNotifyCallback(stream, pollfd) {
583+
var cb = (flags) => {
584+
if (notifyDone) {
585+
return;
678586
}
679-
#endif
680-
return stream.stream_ops.poll(stream, timeoutInMillis);
681-
})();
682-
} else {
683-
#if ASSERTIONS
684-
if (timeoutInMillis != 0) warnOnce('non-zero select() timeout not supported: ' + timeoutInMillis)
685-
#endif
587+
#if RUNTIME_DEBUG
588+
dbg(`async poll notify: stream=${stream}`);
589+
#endif
590+
var events = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.events, 'i16') }}};
591+
flags &= events | {{{ cDefs.POLLERR }}} | {{{ cDefs.POLLHUP }}};
592+
assert(flags)
593+
{{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'flags', 'i16') }}};
594+
asyncPollComplete(1);
595+
}
596+
cb.registerCleanupFunc = (f) => {
597+
if (f) cleanupFuncs.push(f);
598+
}
599+
return cb;
686600
}
687-
688-
fdSet.setFlags(fd, flags);
689-
}
690-
691-
#if PTHREADS
692-
if (makeNotifyCallback != null) {
693-
if ((fdSet.getTotal() > 0) || (timeoutInMillis == 0) ) {
694-
makeNotifyCallback(-1)(0);
601+
if (timeout > 0) {
602+
setTimeout(() => {
603+
#if RUNTIME_DEBUG
604+
dbg('poll: timeout');
605+
#endif
606+
asyncPollComplete(0);
607+
}, timeout);
695608
}
696-
return 0;
697609
}
698610
#endif
699611
700-
fdSet.commit();
701-
702-
return fdSet.getTotal();
703-
},
704-
_msync_js__i53abi: true,
705-
_msync_js: (addr, len, prot, flags, fd, offset) => {
706-
if (isNaN(offset)) return -{{{ cDefs.EOVERFLOW }}};
707-
SYSCALLS.doMsync(addr, SYSCALLS.getStreamFromFD(fd), len, flags, offset);
708-
return 0;
709-
},
710-
__syscall_fdatasync: (fd) => {
711-
var stream = SYSCALLS.getStreamFromFD(fd);
712-
return 0; // we can't do anything synchronously; the in-memory FS is already synced to
713-
},
714-
__syscall_poll: (fds, nfds, timeout) => {
715-
var count = 0;
716612
for (var i = 0; i < nfds; i++) {
717613
var pollfd = fds + {{{ C_STRUCTS.pollfd.__size__ }}} * i;
718614
var fd = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.fd, 'i32') }}};
719615
var events = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.events, 'i16') }}};
720616
var mask = {{{ cDefs.POLLNVAL }}};
721617
var stream = FS.getStream(fd);
722618
if (stream) {
723-
mask = {{{ DEFAULT_POLLMASK }}};
724619
if (stream.stream_ops.poll) {
620+
#if PTHREADS
621+
if (ctx && timeout) {
622+
mask = stream.stream_ops.poll(stream, timeout, makeNotifyCallback(stream, pollfd));
623+
} else
624+
#endif
725625
mask = stream.stream_ops.poll(stream, -1);
626+
} else {
627+
mask = {{{ cDefs.POLLIN | cDefs.POLLOUT }}};
726628
}
727629
}
728630
mask &= events | {{{ cDefs.POLLERR }}} | {{{ cDefs.POLLHUP }}};
729631
if (mask) count++;
730632
{{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}};
731633
}
634+
635+
#if PTHREADS
636+
if (ctx) {
637+
if (count || !timeout) {
638+
asyncPollComplete(count);
639+
}
640+
return 0;
641+
}
642+
#endif
643+
732644
#if ASSERTIONS
733645
if (!count && timeout != 0) warnOnce('non-zero poll() timeout not supported: ' + timeout)
734646
#endif

system/lib/libc/emscripten_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ EmscriptenDeviceOrientationEvent* _emscripten_get_last_deviceorientation_event()
151151
EmscriptenDeviceMotionEvent* _emscripten_get_last_devicemotion_event();
152152
EmscriptenMouseEvent* _emscripten_get_last_mouse_event();
153153

154-
int _newselect_js(void* ctx, void* arg, int n, void *rfds, void *wfds, void *efds, int64_t timeout);
154+
int _poll_js(void* fds, int nfds, int timeout, void* ctx, void* arg);
155155

156156
#ifdef __cplusplus
157157
}

system/lib/libc/musl/arch/emscripten/bits/syscall.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#define SYS_mprotect __syscall_mprotect
2424
#define SYS_getpgid __syscall_getpgid
2525
#define SYS_fchdir __syscall_fchdir
26-
#define SYS__newselect __syscall__newselect
2726
#define SYS_msync __syscall_msync
2827
#define SYS_getsid __syscall_getsid
2928
#define SYS_fdatasync __syscall_fdatasync

system/lib/libc/musl/arch/emscripten/syscall_arch.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ int __syscall_uname(intptr_t buf);
4141
int __syscall_mprotect(size_t addr, size_t len, int prot);
4242
int __syscall_getpgid(int pid);
4343
int __syscall_fchdir(int fd);
44-
int __syscall__newselect(int nfds, intptr_t readfds, intptr_t writefds, intptr_t exceptfds, int64_t timeout);
4544
int __syscall_msync(intptr_t addr, size_t len, int flags);
4645
int __syscall_getsid(int pid);
4746
int __syscall_fdatasync(int fd);

0 commit comments

Comments
 (0)