Skip to content
Merged
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
43 changes: 35 additions & 8 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -2264,14 +2264,41 @@ mergeInto(LibraryManager.library, {
return 0;
},

// http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html
alarm__deps: ['raise', '$callUserCallback'],
alarm: function(seconds) {
setTimeout(function() {
callUserCallback(function() {
_raise({{{ cDefine('SIGALRM') }}});
});
}, seconds*1000);
$timers: {},

// Helper function for setitimer that registers timers with the eventloop.
// Timers always fire on the main thread, either directly from JS (here) or
// or when the main thread is busy waiting calling _emscripten_yield.
_setitimer_js__sig: 'iid',
_setitimer_js__proxy: 'sync',
_setitimer_js__deps: ['$timers', '$callUserCallback',
'_emscripten_timeout', 'emscripten_get_now'],
_setitimer_js: function(which, timeout_ms) {
#if RUNTIME_DEBUG
dbg('setitimer_js ' + which + ' timeout=' + timeout_ms);
#endif
// First, clear any existing timer.
if (timers[which]) {
clearTimeout(timers[which].id);
delete timers[which];
}

// A timeout of zero simply cancels the current timeout so we have nothing
// more to do.
if (!timeout_ms) return 0;

var id = setTimeout(() => {
#if ASSERTIONS
assert(which in timers);
#endif
delete timers[which];
#if RUNTIME_DEBUG
dbg('itimer fired: ' + which);
#endif
callUserCallback(() => __emscripten_timeout(which, _emscripten_get_now()));
}, timeout_ms);
timers[which] = { id: id, timeout_ms: timeout_ms };
return 0;
},

// Helper for raise() to avoid signature mismatch failures:
Expand Down
4 changes: 4 additions & 0 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ if (ENVIRONMENT_IS_SHELL) {
setTimeout(() => onload(readBinary(f)), 0);
};

if (typeof clearTimeout == 'undefined') {
globalThis.clearTimeout = (id) => {};
}

if (typeof scriptArgs != 'undefined') {
arguments_ = scriptArgs;
} else if (typeof arguments != 'undefined') {
Expand Down
2 changes: 0 additions & 2 deletions system/include/emscripten/threading.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,6 @@ void emscripten_check_blocking_allowed(void);
// Experimental API for syncing loaded code between pthreads.
void _emscripten_thread_sync_code();

void _emscripten_yield();

#ifdef __cplusplus
}
#endif
2 changes: 0 additions & 2 deletions system/lib/libc/emscripten_syscall_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,6 @@ UNIMPLEMENTED(pipe2, (intptr_t fds, int flags))
UNIMPLEMENTED(pselect6, (int nfds, intptr_t readfds, intptr_t writefds, intptr_t exceptfds, intptr_t timeout, intptr_t sigmaks))
UNIMPLEMENTED(recvmmsg, (int sockfd, intptr_t msgvec, size_t vlen, int flags, ...))
UNIMPLEMENTED(sendmmsg, (int sockfd, intptr_t msgvec, size_t vlen, int flags, ...))
UNIMPLEMENTED(setitimer, (int which, intptr_t new_value, intptr_t old_value))
UNIMPLEMENTED(getitimer, (int which, intptr_t old_value))
UNIMPLEMENTED(shutdown, (int sockfd, int how, int dummy, int dummy2, int dummy3, int dummy4))
UNIMPLEMENTED(socketpair, (int domain, int type, int protocol, intptr_t fds, int dummy, int dummy2))
UNIMPLEMENTED(wait4,(int pid, intptr_t wstatus, int options, int rusage))
2 changes: 0 additions & 2 deletions system/lib/libc/musl/arch/emscripten/bits/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
#define SYS_fchmod __syscall_fchmod
#define SYS_getpriority __syscall_getpriority
#define SYS_setpriority __syscall_setpriority
#define SYS_setitimer __syscall_setitimer
#define SYS_getitimer __syscall_getitimer
#define SYS_wait4 __syscall_wait4
#define SYS_setdomainname __syscall_setdomainname
#define SYS_uname __syscall_uname
Expand Down
2 changes: 0 additions & 2 deletions system/lib/libc/musl/arch/emscripten/syscall_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ int __syscall_fchmod(int fd, int mode);
int __syscall_getpriority(int which, int who);
int __syscall_setpriority(int which, int who, int prio);
int __syscall_socketcall(int call, intptr_t args);
int __syscall_setitimer(int which, intptr_t new_value, intptr_t old_value);
int __syscall_getitimer(int which, intptr_t old_value);
int __syscall_wait4(int pid, intptr_t wstatus, int options, int rusage);
int __syscall_setdomainname(intptr_t name, size_t size);
int __syscall_uname(intptr_t buf);
Expand Down
4 changes: 3 additions & 1 deletion system/lib/libc/musl/src/sched/sched_yield.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#include "syscall.h"

#if __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#include <emscripten/threading.h>
#include "threading_internal.h"
#endif

int sched_yield()
Expand All @@ -11,7 +13,7 @@ int sched_yield()
// SharedArrayBuffer and wasm threads do not support explicit yielding.
// For now we at least call `emscripten_yield` which processes the event queue
// (along with other essential tasks).
_emscripten_yield();
_emscripten_yield(emscripten_get_now());
return 0;
#else
return syscall(SYS_sched_yield);
Expand Down
11 changes: 11 additions & 0 deletions system/lib/libc/musl/src/signal/getitimer.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#include <sys/time.h>
#include "syscall.h"

#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
void __getitimer(int which, struct itimerval *old, double now);
#endif

int getitimer(int which, struct itimerval *old)
{
#ifdef __EMSCRIPTEN__
if (which > ITIMER_PROF) return EINVAL;
__getitimer(which, old, emscripten_get_now());
return 0;
#else
if (sizeof(time_t) > sizeof(long)) {
long old32[4];
int r = __syscall(SYS_getitimer, which, old32);
Expand All @@ -15,4 +25,5 @@ int getitimer(int which, struct itimerval *old)
return __syscall_ret(r);
}
return syscall(SYS_getitimer, which, old);
#endif
}
73 changes: 73 additions & 0 deletions system/lib/libc/musl/src/signal/setitimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,80 @@

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

#ifdef __EMSCRIPTEN__
#include <signal.h>
#include <stdio.h>
#include <emscripten/emscripten.h>

// Timeouts can either fire directly from the JS event loop (which calls
// `_emscripten_timeout`), or from `_emscripten_check_timers` (which is called
// from `_emscripten_yield`). In order to be able to check the timers here we
// cache the current timeout and interval for each the 3 types of timer
// (ITIMER_PROF/ITIMER_VIRTUAL/ITIMER_REAL).
Comment thread
sbc100 marked this conversation as resolved.
static double current_timeout_ms[3];
static double current_intervals_ms[3];

#define MAX(a,b) ((a)>(b)?(a):(b))

int _setitimer_js(int which, double timeout);

void __getitimer(int which, struct itimerval *old, double now)
{
double remaining_ms = MAX(current_timeout_ms[which] - now, 0);
old->it_value.tv_sec = remaining_ms / 1000;
old->it_value.tv_usec = remaining_ms * 1000;
old->it_interval.tv_sec = current_intervals_ms[which] / 1000;
old->it_interval.tv_usec = current_intervals_ms[which] * 1000;
}

void _emscripten_timeout(int which, double now)
{
int signum = SIGALRM;
if (which == ITIMER_PROF)
signum = SIGPROF;
else if (which == ITIMER_VIRTUAL)
signum = SIGVTALRM;
int next_timeout = current_intervals_ms[which];
if (next_timeout)
current_timeout_ms[which] = now + next_timeout;
else
current_timeout_ms[which] = 0;
_setitimer_js(which, next_timeout);
raise(signum);
}

void _emscripten_check_timers(double now)
{
for (int which = 0; which < 3; which++) {
if (current_timeout_ms[which]) {
// Only call out to JS to get the current time if it was not passed in
// *and* we have one or more timers set.
if (!now)
now = emscripten_get_now();
if (now >= current_timeout_ms[which])
_emscripten_timeout(which, now);
}
}
}
#endif

int setitimer(int which, const struct itimerval *restrict new, struct itimerval *restrict old)
{
#ifdef __EMSCRIPTEN__
if (which > ITIMER_PROF) return EINVAL;
double now = emscripten_get_now();
if (old) {
__getitimer(which, old, now);
}
if (new->it_value.tv_sec || new->it_value.tv_usec) {
current_timeout_ms[which] = now + new->it_value.tv_sec * 1000 + new->it_value.tv_usec / 1000;
current_intervals_ms[which] = new->it_interval.tv_sec * 1000 + new->it_interval.tv_usec / 1000;
} else {
current_timeout_ms[which] = 0;
current_intervals_ms[which] = 0;
}
return _setitimer_js(which, new->it_value.tv_sec * 1000 + new->it_value.tv_usec / 1000);
#else
if (sizeof(time_t) > sizeof(long)) {
time_t is = new->it_interval.tv_sec, vs = new->it_value.tv_sec;
long ius = new->it_interval.tv_usec, vus = new->it_value.tv_usec;
Expand All @@ -23,4 +95,5 @@ int setitimer(int which, const struct itimerval *restrict new, struct itimerval
return __syscall_ret(r);
}
return syscall(SYS_setitimer, which, new, old);
#endif
}
7 changes: 5 additions & 2 deletions system/lib/pthread/emscripten_futex_wait.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static int futex_wait_main_browser_thread(volatile void* addr,
// We were told to stop waiting, so stop.
break;
}
_emscripten_yield();
_emscripten_yield(now);

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

_emscripten_yield();
// Pass 0 here, which means we don't have access to the current time in this
// function. This tells _emscripten_yield to call emscripten_get_now if (and
// only if) it needs to know the time.
_emscripten_yield(0);
Comment thread
sbc100 marked this conversation as resolved.
Comment thread
sbc100 marked this conversation as resolved.

int ret;
emscripten_conditional_set_current_thread_status(EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_WAITFUTEX);
Expand Down
15 changes: 9 additions & 6 deletions system/lib/pthread/emscripten_yield.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ static void dummy()
{
}

static void dummy2(double now)
{
}

weak_alias(dummy, _emscripten_thread_sync_code);
weak_alias(dummy2, _emscripten_check_timers);

/*
* Called whenever a thread performs a blocking action (or calls sched_yield).
* This function takes care of running the event queue and other housekeeping
* tasks.
*/
void _emscripten_yield() {
void _emscripten_yield(double now) {
Comment thread
sbc100 marked this conversation as resolved.
int is_runtime_thread = emscripten_is_main_runtime_thread();

// When a secondary thread crashes, we need to be able to interrupt the main
Expand All @@ -37,6 +37,9 @@ void _emscripten_yield() {
emscripten_unwind_to_js_event_loop();
}

// This is no-op in programs that don't include use of itimer/alarm.
_emscripten_check_timers(now);

// Assist other threads by executing proxied operations that are effectively
// singlethreaded.
emscripten_main_thread_process_queued_calls();
Expand Down
18 changes: 13 additions & 5 deletions system/lib/pthread/library_pthread_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ void emscripten_current_thread_process_queued_calls() {
// nop
}

void _emscripten_yield() {
// nop
static void dummy(double now)
{
}

weak_alias(dummy, _emscripten_check_timers);

void _emscripten_yield(double now) {
_emscripten_check_timers(now);
}

int pthread_mutex_init(
Expand Down Expand Up @@ -403,7 +409,9 @@ void __unlock(void* ptr) {}
// proper sleeps, so simulate a busy spin wait loop instead.
void emscripten_thread_sleep(double msecs) {
double start = emscripten_get_now();
while (emscripten_get_now() - start < msecs) {
// Do nothing.
}
double now = start;
do {
_emscripten_yield(now);
now = emscripten_get_now();
} while (now - start < msecs);
}
10 changes: 10 additions & 0 deletions system/lib/pthread/threading_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ typedef struct thread_profiler_block {
char name[EM_THREAD_NAME_MAX];
} thread_profiler_block;

// Called whenever a thread performs a blocking action (or calls sched_yield).
// This function takes care of running the event queue and other housekeeping
// tasks.
//
// If that caller already know the current time it can pass it vai the now
// argument. This can save _emscripten_check_timers from needing to call out to
// JS to get the current time. Passing 0 means that caller doesn't know the
// the current time.
void _emscripten_yield(double now);

void __emscripten_init_main_thread_js(void* tb);
void _emscripten_thread_profiler_enable();
void __emscripten_thread_cleanup(pthread_t thread);
Expand Down
Loading