Skip to content

Commit ddd211c

Browse files
committed
Remove custom alarm impl in favor of lower level itimer syscalls
These alarms/signals will now fire if when waiting on a lock (i.e. they don't depend on the event loops running). Fixes: #12415
1 parent 53568c0 commit ddd211c

File tree

19 files changed

+281
-31
lines changed

19 files changed

+281
-31
lines changed

src/library.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,14 +2264,41 @@ mergeInto(LibraryManager.library, {
22642264
return 0;
22652265
},
22662266

2267-
// http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html
2268-
alarm__deps: ['raise', '$callUserCallback'],
2269-
alarm: function(seconds) {
2270-
setTimeout(function() {
2271-
callUserCallback(function() {
2272-
_raise({{{ cDefine('SIGALRM') }}});
2273-
});
2274-
}, seconds*1000);
2267+
$timers: {},
2268+
2269+
// Helper function for setitimer that registers timers with the eventloop.
2270+
// Timers always fire on the main thread, either directly from JS (here) or
2271+
// or when the main thread is busy waiting calling _emscripten_yield.
2272+
_setitimer_js__sig: 'iid',
2273+
_setitimer_js__proxy: 'sync',
2274+
_setitimer_js__deps: ['$timers', '$callUserCallback',
2275+
'_emscripten_timeout', 'emscripten_get_now'],
2276+
_setitimer_js: function(which, timeout_ms) {
2277+
#if RUNTIME_DEBUG
2278+
dbg('setitimer_js ' + which + ' timeout=' + timeout_ms);
2279+
#endif
2280+
// First, clear any existing timer.
2281+
if (timers[which]) {
2282+
clearTimeout(timers[which].id);
2283+
delete timers[which];
2284+
}
2285+
2286+
// A timeout of zero simply cancels the current timeout so we have nothing
2287+
// more to do.
2288+
if (!timeout_ms) return 0;
2289+
2290+
var id = setTimeout(() => {
2291+
#if ASSERTIONS
2292+
assert(which in timers);
2293+
#endif
2294+
delete timers[which];
2295+
#if RUNTIME_DEBUG
2296+
dbg('itimer fired: ' + which);
2297+
#endif
2298+
callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now()));
2299+
}, timeout_ms);
2300+
timers[which] = { id: id, timeout_ms: timeout_ms };
2301+
return 0;
22752302
},
22762303

22772304
// Helper for raise() to avoid signature mismatch failures:

src/shell.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ if (ENVIRONMENT_IS_SHELL) {
318318
setTimeout(() => onload(readBinary(f)), 0);
319319
};
320320

321+
if (typeof clearTimeout == 'undefined') {
322+
globalThis.clearTimeout = (id) => {};
323+
}
324+
321325
if (typeof scriptArgs != 'undefined') {
322326
arguments_ = scriptArgs;
323327
} else if (typeof arguments != 'undefined') {

system/include/emscripten/threading.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,6 @@ void emscripten_check_blocking_allowed(void);
289289
// Experimental API for syncing loaded code between pthreads.
290290
void _emscripten_thread_sync_code();
291291

292-
void _emscripten_yield();
293-
294292
#ifdef __cplusplus
295293
}
296294
#endif

system/lib/libc/emscripten_syscall_stubs.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ UNIMPLEMENTED(pipe2, (intptr_t fds, int flags))
254254
UNIMPLEMENTED(pselect6, (int nfds, intptr_t readfds, intptr_t writefds, intptr_t exceptfds, intptr_t timeout, intptr_t sigmaks))
255255
UNIMPLEMENTED(recvmmsg, (int sockfd, intptr_t msgvec, size_t vlen, int flags, ...))
256256
UNIMPLEMENTED(sendmmsg, (int sockfd, intptr_t msgvec, size_t vlen, int flags, ...))
257-
UNIMPLEMENTED(setitimer, (int which, intptr_t new_value, intptr_t old_value))
258-
UNIMPLEMENTED(getitimer, (int which, intptr_t old_value))
259257
UNIMPLEMENTED(shutdown, (int sockfd, int how, int dummy, int dummy2, int dummy3, int dummy4))
260258
UNIMPLEMENTED(socketpair, (int domain, int type, int protocol, intptr_t fds, int dummy, int dummy2))
261259
UNIMPLEMENTED(wait4,(int pid, intptr_t wstatus, int options, int rusage))

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
#define SYS_fchmod __syscall_fchmod
2121
#define SYS_getpriority __syscall_getpriority
2222
#define SYS_setpriority __syscall_setpriority
23-
#define SYS_setitimer __syscall_setitimer
24-
#define SYS_getitimer __syscall_getitimer
2523
#define SYS_wait4 __syscall_wait4
2624
#define SYS_setdomainname __syscall_setdomainname
2725
#define SYS_uname __syscall_uname

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ int __syscall_fchmod(int fd, int mode);
3838
int __syscall_getpriority(int which, int who);
3939
int __syscall_setpriority(int which, int who, int prio);
4040
int __syscall_socketcall(int call, intptr_t args);
41-
int __syscall_setitimer(int which, intptr_t new_value, intptr_t old_value);
42-
int __syscall_getitimer(int which, intptr_t old_value);
4341
int __syscall_wait4(int pid, intptr_t wstatus, int options, int rusage);
4442
int __syscall_setdomainname(intptr_t name, size_t size);
4543
int __syscall_uname(intptr_t buf);

system/lib/libc/musl/src/sched/sched_yield.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#include "syscall.h"
33

44
#if __EMSCRIPTEN__
5+
#include <emscripten/emscripten.h>
56
#include <emscripten/threading.h>
7+
#include "threading_internal.h"
68
#endif
79

810
int sched_yield()
@@ -11,7 +13,7 @@ int sched_yield()
1113
// SharedArrayBuffer and wasm threads do not support explicit yielding.
1214
// For now we at least call `emscripten_yield` which processes the event queue
1315
// (along with other essential tasks).
14-
_emscripten_yield();
16+
_emscripten_yield(emscripten_get_now());
1517
return 0;
1618
#else
1719
return syscall(SYS_sched_yield);

system/lib/libc/musl/src/signal/getitimer.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
#include <sys/time.h>
22
#include "syscall.h"
33

4+
#ifdef __EMSCRIPTEN__
5+
#include <emscripten/emscripten.h>
6+
void __getitimer(int which, struct itimerval *old, double now);
7+
#endif
8+
49
int getitimer(int which, struct itimerval *old)
510
{
11+
#ifdef __EMSCRIPTEN__
12+
if (which > ITIMER_PROF) return EINVAL;
13+
__getitimer(which, old, emscripten_get_now());
14+
return 0;
15+
#else
616
if (sizeof(time_t) > sizeof(long)) {
717
long old32[4];
818
int r = __syscall(SYS_getitimer, which, old32);
@@ -15,4 +25,5 @@ int getitimer(int which, struct itimerval *old)
1525
return __syscall_ret(r);
1626
}
1727
return syscall(SYS_getitimer, which, old);
28+
#endif
1829
}

system/lib/libc/musl/src/signal/setitimer.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,78 @@
44

55
#define IS32BIT(x) !((x)+0x80000000ULL>>32)
66

7+
#ifdef __EMSCRIPTEN__
8+
#include <signal.h>
9+
#include <stdio.h>
10+
#include <emscripten/emscripten.h>
11+
12+
// Timeouts can either fire directly from the JS event loop (which calls
13+
// `_emscripten_timeout`), or from `_emscripten_check_timers` (which is called
14+
// from `_emscripten_yeild`). In order to be able to check the timers here we
15+
// cache the current timeout and intervale for each the 3 types of timer
16+
// (ITIMER_PROF/ITIMER_VIRTUAL/ITIMER_REAL).
17+
static double current_timeout_ms[3];
18+
static double current_intervals_ms[3];
19+
20+
#define MAX(a,b) ((a)>(b)?(a):(b))
21+
22+
int _setitimer_js(int which, double timeout);
23+
24+
void __getitimer(int which, struct itimerval *old, double now)
25+
{
26+
double remaining_ms = MAX(current_timeout_ms[which] - now, 0);
27+
old->it_value.tv_sec = remaining_ms / 1000;
28+
old->it_value.tv_usec = remaining_ms * 1000;
29+
old->it_interval.tv_sec = current_intervals_ms[which] / 1000;
30+
old->it_interval.tv_usec = current_intervals_ms[which] * 1000;
31+
}
32+
33+
void _emscripten_timeout(int which, double now)
34+
{
35+
int signum = SIGALRM;
36+
if (which == ITIMER_PROF) {
37+
signum = SIGPROF;
38+
} else if (which == ITIMER_VIRTUAL) {
39+
signum = SIGVTALRM;
40+
}
41+
int next_timeout = current_intervals_ms[which];
42+
if (next_timeout) {
43+
current_timeout_ms[which] = now + next_timeout;
44+
} else {
45+
current_timeout_ms[which] = 0;
46+
}
47+
_setitimer_js(which, next_timeout);
48+
raise(signum);
49+
}
50+
51+
void _emscripten_check_timers(double now)
52+
{
53+
if (!now) now = emscripten_get_now();
54+
for (int which = 0; which < 3; which++) {
55+
if (current_timeout_ms[which] && now >= current_timeout_ms[which]) {
56+
_emscripten_timeout(which, now);
57+
}
58+
}
59+
}
60+
#endif
61+
762
int setitimer(int which, const struct itimerval *restrict new, struct itimerval *restrict old)
863
{
64+
#ifdef __EMSCRIPTEN__
65+
if (which > ITIMER_PROF) return EINVAL;
66+
double now = emscripten_get_now();
67+
if (old) {
68+
__getitimer(which, old, now);
69+
}
70+
if (new->it_value.tv_sec || new->it_value.tv_usec) {
71+
current_timeout_ms[which] = now + new->it_value.tv_sec * 1000 + new->it_value.tv_usec / 1000;
72+
current_intervals_ms[which] = new->it_interval.tv_sec * 1000 + new->it_interval.tv_usec / 1000;
73+
} else {
74+
current_timeout_ms[which] = 0;
75+
current_intervals_ms[which] = 0;
76+
}
77+
return _setitimer_js(which, new->it_value.tv_sec * 1000 + new->it_value.tv_usec / 1000);
78+
#else
979
if (sizeof(time_t) > sizeof(long)) {
1080
time_t is = new->it_interval.tv_sec, vs = new->it_value.tv_sec;
1181
long ius = new->it_interval.tv_usec, vus = new->it_value.tv_usec;
@@ -23,4 +93,5 @@ int setitimer(int which, const struct itimerval *restrict new, struct itimerval
2393
return __syscall_ret(r);
2494
}
2595
return syscall(SYS_setitimer, which, new, old);
96+
#endif
2697
}

system/lib/pthread/emscripten_futex_wait.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int futex_wait_main_browser_thread(volatile void* addr,
6464
// We were told to stop waiting, so stop.
6565
break;
6666
}
67-
_emscripten_yield();
67+
_emscripten_yield(now);
6868

6969
// Check the value, as if we were starting the futex all over again.
7070
// This handles the following case:
@@ -116,7 +116,7 @@ int emscripten_futex_wait(volatile void *addr, uint32_t val, double max_wait_ms)
116116
return -EINVAL;
117117
}
118118

119-
_emscripten_yield();
119+
_emscripten_yield(0);
120120

121121
int ret;
122122
emscripten_conditional_set_current_thread_status(EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_WAITFUTEX);

0 commit comments

Comments
 (0)