Skip to content

Commit ffd1bbe

Browse files
committed
select: enable to handle timeout
This commit enables the select syscall to handle timeout with multiple event sources when it is called from a worker. When a thread worker calls __syscall_newselect, it blocks using emscripten_proxy_sync_with_ctx. When __syscall_newselect is called with zero timeout, it is unblocked immediately by calling emscripten_proxy_finish before returning. When it is called with non-zero timeout, emscripten_proxy_finish is invoked either by the underlying stream implementation (where an event occurs) or by a setTimeout callback when the tiemout expires. In proxying.c, several wrapper functions for proxying-related APIs are added to allow the JS implementation of newselect to use them. Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
1 parent e7f0c89 commit ffd1bbe

File tree

8 files changed

+123
-2
lines changed

8 files changed

+123
-2
lines changed

src/lib/libsigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ sigs = {
381381
_mmap_js__sig: 'ipiiijpp',
382382
_msync_js__sig: 'ippiiij',
383383
_munmap_js__sig: 'ippiiij',
384+
_newselect_js__sig: 'ippipppp',
384385
_setitimer_js__sig: 'iid',
385386
_timegm_js__sig: 'jp',
386387
_tzset_js__sig: 'vpppp',

src/lib/libsyscall.js

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,12 +609,31 @@ var SyscallsLibrary = {
609609
FS.chdir(stream.path);
610610
return 0;
611611
},
612+
__syscall__newselect__proxy: 'none',
613+
__syscall__newselect__deps: ['_newselect_js',
614+
#if PTHREADS
615+
'_emscripten_proxy_newselect',
616+
#endif
617+
],
612618
__syscall__newselect: (nfds, readfds, writefds, exceptfds, timeout) => {
619+
#if PTHREADS
620+
if (ENVIRONMENT_IS_PTHREAD) {
621+
return __emscripten_proxy_newselect(nfds, readfds, writefds, exceptfds, timeout);
622+
}
623+
#endif
624+
return __newselect_js(null, null, nfds, readfds, writefds, exceptfds, timeout);
625+
},
626+
_newselect_js__proxy: 'none',
627+
#if PTHREADS
628+
_newselect_js__deps: ['_emscripten_proxy_newselect_finish'],
629+
#endif
630+
_newselect_js: (ctx, arg, nfds, readfds, writefds, exceptfds, timeout) => {
613631
// readfds are supported,
614632
// writefds checks socket open status
615633
// exceptfds are supported, although on web, such exceptional conditions never arise in web sockets
616634
// and so the exceptfds list will always return empty.
617-
// timeout is supported, although on SOCKFS and PIPEFS these are ignored and always treated as 0 - fully async
635+
// timeout is supported, although on SOCKFS these are ignored and always treated as 0 - fully async
636+
// and PIPEFS supports timeout only when the select is called from a worker.
618637
#if ASSERTIONS
619638
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
620639
#endif
@@ -631,6 +650,36 @@ var SyscallsLibrary = {
631650
timeoutInMillis = SYSCALLS.getTimeoutInMillis(timeout);
632651
}
633652

653+
#if PTHREADS
654+
var makeNotifyCallback = null;
655+
if (ctx != null) {
656+
// Enable event handlers only when the select call is proxied from a worker.
657+
var cleanupFuncs = [];
658+
var notifyDone = false;
659+
makeNotifyCallback = (fd) => {
660+
var cb = (flags) => {
661+
if (notifyDone) {
662+
return;
663+
}
664+
if (fd >= 0) {
665+
fdSet.setFlags(fd, flags);
666+
}
667+
notifyDone = true;
668+
cleanupFuncs.forEach(cb => cb());
669+
fdSet.commit();
670+
__emscripten_proxy_newselect_finish({{{ to64('ctx') }}}, {{{ to64('arg') }}}, fdSet.getTotal());
671+
}
672+
cb.registerCleanupFunc = (f) => {
673+
if (f != null) cleanupFuncs.push(f);
674+
}
675+
return cb;
676+
}
677+
if (timeoutInMillis > 0) {
678+
setTimeout(() => makeNotifyCallback(-1)(0), timeoutInMillis);
679+
}
680+
}
681+
#endif
682+
634683
for (var fd = 0; fd < nfds; fd++) {
635684
var mask = 1 << (fd % 32);
636685
if (!(check(fd, allLow, allHigh, mask))) {
@@ -642,12 +691,27 @@ var SyscallsLibrary = {
642691
var flags = SYSCALLS.DEFAULT_POLLMASK;
643692

644693
if (stream.stream_ops.poll) {
645-
flags = stream.stream_ops.poll(stream, ((timeoutInMillis < 0) || readfds) ? timeoutInMillis : 0);
694+
flags = (() => {
695+
#if PTHREADS
696+
if (makeNotifyCallback != null) {
697+
return stream.stream_ops.poll(stream, timeoutInMillis, timeoutInMillis != 0 ? makeNotifyCallback(fd) : null);
698+
}
699+
#endif
700+
return stream.stream_ops.poll(stream, ((timeoutInMillis < 0) || readfds) ? timeoutInMillis : 0);
701+
})();
646702
}
647703

648704
fdSet.setFlags(fd, flags);
649705
}
650706

707+
#if PTHREADS
708+
if (makeNotifyCallback != null) {
709+
if ((fdSet.getTotal() > 0) || (timeoutInMillis == 0) ) {
710+
makeNotifyCallback(-1)(0);
711+
}
712+
return 0;
713+
}
714+
#endif
651715

652716
fdSet.commit();
653717

system/lib/libc/emscripten_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ 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, void *tv);
155+
154156
#ifdef __cplusplus
155157
}
156158
#endif

system/lib/libc/proxying_select.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <assert.h>
9+
#include <emscripten/proxying.h>
10+
#include <emscripten/threading.h>
11+
12+
#include "emscripten_internal.h"
13+
14+
typedef struct proxied_select_t {
15+
int n;
16+
void *rfds;
17+
void *wfds;
18+
void *efds;
19+
void *tv;
20+
int result;
21+
} proxied_select_t;
22+
23+
static void call_newselect(em_proxying_ctx* ctx, void* arg) {
24+
proxied_select_t* t = arg;
25+
_newselect_js(ctx, arg, t->n, t->rfds, t->wfds, t->efds, t->tv);
26+
}
27+
28+
void _emscripten_proxy_newselect_finish(em_proxying_ctx* ctx, void* arg, int ret) {
29+
proxied_select_t* t = arg;
30+
t->result = ret;
31+
emscripten_proxy_finish(ctx);
32+
}
33+
34+
int _emscripten_proxy_newselect(int n, void *rfds, void *wfds, void *efds, void *tv) {
35+
em_proxying_queue* q = emscripten_proxy_get_system_queue();
36+
pthread_t target = emscripten_main_runtime_thread_id();
37+
proxied_select_t t = {.n = n, .rfds = rfds, .wfds = wfds, .efds = efds, .tv = tv};
38+
if (!emscripten_proxy_sync_with_ctx(q, target, call_newselect, &t)) {
39+
assert(false && "emscripten_proxy_sync failed");
40+
return -1;
41+
}
42+
return t.result;
43+
}

system/lib/standalone/standalone.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ weak int _munmap_js(
6666
return -ENOSYS;
6767
}
6868

69+
weak int _newselect_js(void* ctx, void* arg, int n, void *rfds, void *wfds, void *efds, void *tv) {
70+
return -ENOSYS;
71+
}
72+
6973
// open(), etc. - we just support the standard streams, with no
7074
// corner case error checking; everything else is not permitted.
7175
// TODO: full file support for WASI, or an option for it

system/lib/wasmfs/syscalls.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,4 +1780,8 @@ int __syscall__newselect(int nfds,
17801780
return -ENOMEM;
17811781
}
17821782

1783+
int _newselect_js(void* ctx, void* arg, int n, void *rfds, void *wfds, void *efds, void *tv) {
1784+
return -ENOSYS;
1785+
}
1786+
17831787
} // extern "C"

tools/emscripten.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,8 @@ def create_pointer_conversion_wrappers(metadata):
11841184
'_emscripten_dlsync_self_async': '_p',
11851185
'_emscripten_proxy_dlsync_async': '_pp',
11861186
'_emscripten_wasm_worker_initialize': '_p_',
1187+
'_emscripten_proxy_newselect': '__pppp',
1188+
'_emscripten_proxy_newselect_finish': '_pp_',
11871189
'_wasmfs_rename': '_pp',
11881190
'_wasmfs_readlink': '_pp',
11891191
'_wasmfs_truncate': '_p_',

tools/system_libs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,7 @@ def get_files(self):
13671367
'sigtimedwait.c',
13681368
'wasi-helpers.c',
13691369
'system.c',
1370+
'proxying_select.c',
13701371
])
13711372

13721373
if settings.RELOCATABLE or settings.MAIN_MODULE:

0 commit comments

Comments
 (0)