Skip to content

Commit fa8fc29

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 fa8fc29

File tree

16 files changed

+401
-322
lines changed

16 files changed

+401
-322
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: 70 additions & 156 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,116 +541,75 @@ 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
560+
_poll_js: (fds, nfds, timeout, ctx, arg) => {
621561
#if PTHREADS
622-
assert(!ENVIRONMENT_IS_PTHREAD, '_newselect_js must be called in the main thread');
562+
// Enable event handlers only when the poll call is proxied from a worker.
563+
var cleanupFuncs = [];
564+
var notifyDone = false;
565+
function asyncPollComplete(count) {
566+
if (notifyDone) {
567+
return;
568+
}
569+
notifyDone = true;
570+
#if RUNTIME_DEBUG
571+
dbg('asyncPollComplete', count);
572+
#endif
573+
cleanupFuncs.forEach(cb => cb());
574+
__emscripten_proxy_poll_finish(ctx, arg, count);
575+
}
576+
function makeNotifyCallback(stream, pollfd) {
577+
var cb = (flags) => {
578+
if (notifyDone) {
579+
return;
580+
}
581+
#if RUNTIME_DEBUG
582+
dbg(`async poll notify: stream=${stream}`);
623583
#endif
584+
var events = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.events, 'i16') }}};
585+
flags &= events | {{{ cDefs.POLLERR }}} | {{{ cDefs.POLLHUP }}};
586+
#if ASSERTIONS
587+
assert(flags)
624588
#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);
632-
633-
#if PTHREADS
634-
var makeNotifyCallback = null;
635-
if (ctx) {
636-
// Enable event handlers only when the select call is proxied from a worker.
637-
var cleanupFuncs = [];
638-
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;
589+
{{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'flags', 'i16') }}};
590+
asyncPollComplete(1);
656591
}
657-
if (timeoutInMillis > 0) {
658-
setTimeout(() => makeNotifyCallback(-1)(0), timeoutInMillis);
592+
cb.registerCleanupFunc = (f) => {
593+
if (f) cleanupFuncs.push(f);
659594
}
595+
return cb;
660596
}
661-
#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
667-
}
668-
669-
var stream = SYSCALLS.getStreamFromFD(fd);
670-
671-
var flags = {{{ DEFAULT_POLLMASK }}};
672597
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);
678-
}
598+
if (ctx) {
599+
#if RUNTIME_DEBUG
600+
dbg('async poll start');
679601
#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)
602+
if (timeout > 0) {
603+
setTimeout(() => {
604+
#if RUNTIME_DEBUG
605+
dbg('poll: timeout');
685606
#endif
607+
asyncPollComplete(0);
608+
}, timeout);
686609
}
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);
695-
}
696-
return 0;
697610
}
698611
#endif
699612
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) => {
715613
var count = 0;
716614
for (var i = 0; i < nfds; i++) {
717615
var pollfd = fds + {{{ C_STRUCTS.pollfd.__size__ }}} * i;
@@ -720,15 +618,31 @@ var SyscallsLibrary = {
720618
var mask = {{{ cDefs.POLLNVAL }}};
721619
var stream = FS.getStream(fd);
722620
if (stream) {
723-
mask = {{{ DEFAULT_POLLMASK }}};
724621
if (stream.stream_ops.poll) {
622+
#if PTHREADS
623+
if (ctx && timeout) {
624+
mask = stream.stream_ops.poll(stream, timeout, makeNotifyCallback(stream, pollfd));
625+
} else
626+
#endif
725627
mask = stream.stream_ops.poll(stream, -1);
628+
} else {
629+
mask = {{{ cDefs.POLLIN | cDefs.POLLOUT }}};
726630
}
727631
}
728632
mask &= events | {{{ cDefs.POLLERR }}} | {{{ cDefs.POLLHUP }}};
729633
if (mask) count++;
730634
{{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}};
731635
}
636+
637+
#if PTHREADS
638+
if (ctx) {
639+
if (count || !timeout) {
640+
asyncPollComplete(count);
641+
}
642+
return 0;
643+
}
644+
#endif
645+
732646
#if ASSERTIONS
733647
if (!count && timeout != 0) warnOnce('non-zero poll() timeout not supported: ' + timeout)
734648
#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)