From a74a6fa1b3e52627b4d2627303386abb44f6a42b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 13:44:22 +0200 Subject: [PATCH 01/18] Support os.Worker on Windows Add thread create/join utility functions and use those to implement os.Worker on the operating system from Redmond. --- cutils.c | 65 ++++++++++++++++++++++++++++++++++++++++ cutils.h | 11 +++++++ quickjs-libc.c | 34 ++++++++------------- run-test262.c | 71 ++++++++------------------------------------ tests/test_worker.js | 4 +-- 5 files changed, 101 insertions(+), 84 deletions(-) diff --git a/cutils.c b/cutils.c index 438087d06..3a23eb0e1 100644 --- a/cutils.c +++ b/cutils.c @@ -1267,6 +1267,36 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { return -1; } +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + HANDLE h, cp; + + *thrd = INVALID_HANDLE_VALUE; + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + cp = GetCurrentProcess(); + h = (HANDLE)_beginthread(start, /*stacksize*/2<<20, arg); + if (!h) + return -1; + // _endthread() automatically closes the handle but we want to wait on + // it so make a copy. Race-y for very short-lived threads. Can be solved + // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing + // |start| from __cdecl to __stdcall. + if (!(flags & JS_THREAD_CREATE_DETACHED)) + if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS)) + return -1; + return 0; +} + +int js_thread_join(js_thread_t thrd) +{ + if (WaitForSingleObject(thrd, INFINITE)) + return -1; + CloseHandle(thrd); + return 0; +} + #else /* !defined(_WIN32) */ void js_once(js_once_t *guard, void (*callback)(void)) { @@ -1409,6 +1439,41 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { #endif +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags) +{ + union { + void (*x)(void *); + void *(*f)(void *); + } u = {start}; + pthread_attr_t attr; + int ret; + + if (flags & ~JS_THREAD_CREATE_DETACHED) + return -1; + if (pthread_attr_init(&attr)) + return -1; + ret = -1; + if (pthread_attr_setstacksize(&attr, 2<<20)) + goto fail; + if (flags & JS_THREAD_CREATE_DETACHED) + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) + goto fail; + if (pthread_create(thrd, &attr, u.f, arg)) + goto fail; + ret = 0; +fail: + pthread_attr_destroy(&attr); + return ret; +} + +int js_thread_join(js_thread_t thrd) +{ + if (pthread_join(thrd, NULL)) + return -1; + return 0; +} + #endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ #ifdef __GNUC__ diff --git a/cutils.h b/cutils.h index 4639a6b93..bf95a30c5 100644 --- a/cutils.h +++ b/cutils.h @@ -570,11 +570,13 @@ static inline size_t js__malloc_usable_size(const void *ptr) typedef INIT_ONCE js_once_t; typedef CRITICAL_SECTION js_mutex_t; typedef CONDITION_VARIABLE js_cond_t; +typedef HANDLE js_thread_t; #else #define JS_ONCE_INIT PTHREAD_ONCE_INIT typedef pthread_once_t js_once_t; typedef pthread_mutex_t js_mutex_t; typedef pthread_cond_t js_cond_t; +typedef pthread_t js_thread_t; #endif void js_once(js_once_t *guard, void (*callback)(void)); @@ -591,6 +593,15 @@ void js_cond_broadcast(js_cond_t *cond); void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout); +enum { + JS_THREAD_CREATE_DETACHED = 1, +}; + +// creates threads with 2 MB stacks (glibc default) +int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, + int flags); +int js_thread_join(js_thread_t thrd); + #endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ #ifdef __cplusplus diff --git a/quickjs-libc.c b/quickjs-libc.c index f9af760e3..4a469ee62 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -80,13 +80,12 @@ extern char **environ; #endif /* _WIN32 */ -#if !defined(_WIN32) && !defined(__wasi__) +#if !defined(__wasi__) /* enable the os.Worker API. IT relies on POSIX threads */ #define USE_WORKER #endif #ifdef USE_WORKER -#include #include "quickjs-c-atomics.h" #endif @@ -143,7 +142,7 @@ typedef struct { typedef struct { int ref_count; #ifdef USE_WORKER - pthread_mutex_t mutex; + js_mutex_t mutex; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ int read_fd; @@ -2461,7 +2460,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JSWorkerMessage *msg; JSValue obj, data_obj, func, retval; - pthread_mutex_lock(&ps->mutex); + js_mutex_lock(&ps->mutex); if (!list_empty(&ps->msg_queue)) { el = ps->msg_queue.next; msg = list_entry(el, JSWorkerMessage, link); @@ -2481,7 +2480,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } } - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); @@ -2511,7 +2510,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } ret = 1; } else { - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); ret = 0; } return ret; @@ -3499,7 +3498,7 @@ static JSWorkerMessagePipe *js_new_message_pipe(void) } ps->ref_count = 1; init_list_head(&ps->msg_queue); - pthread_mutex_init(&ps->mutex, NULL); + js_mutex_init(&ps->mutex); ps->read_fd = pipe_fds[0]; ps->write_fd = pipe_fds[1]; return ps; @@ -3539,7 +3538,7 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) msg = list_entry(el, JSWorkerMessage, link); js_free_message(msg); } - pthread_mutex_destroy(&ps->mutex); + js_mutex_destroy(&ps->mutex); close(ps->read_fd); close(ps->write_fd); free(ps); @@ -3573,7 +3572,7 @@ static JSClassDef js_worker_class = { .finalizer = js_worker_finalizer, }; -static void *worker_func(void *opaque) +static void worker_func(void *opaque) { WorkerFuncArgs *args = opaque; JSRuntime *rt; @@ -3620,7 +3619,6 @@ static void *worker_func(void *opaque) js_std_free_handlers(rt); JS_FreeContext(ctx); JS_FreeRuntime(rt); - return NULL; } static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, @@ -3662,8 +3660,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, { JSRuntime *rt = JS_GetRuntime(ctx); WorkerFuncArgs *args = NULL; - pthread_t tid; - pthread_attr_t attr; + js_thread_t thr; JSValue obj = JS_UNDEFINED; int ret; const char *filename = NULL, *basename; @@ -3710,14 +3707,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(obj)) goto fail; - pthread_attr_init(&attr); - /* no join at the end */ - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - // musl libc gives threads 80 kb stacks, much smaller than - // JS_DEFAULT_STACK_SIZE (1 MB) - pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default - ret = pthread_create(&tid, &attr, worker_func, args); - pthread_attr_destroy(&attr); + ret = js_thread_create(&thr, worker_func, args, JS_THREAD_CREATE_DETACHED); if (ret != 0) { JS_ThrowTypeError(ctx, "could not create worker"); goto fail; @@ -3792,7 +3782,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, } ps = worker->send_pipe; - pthread_mutex_lock(&ps->mutex); + js_mutex_lock(&ps->mutex); /* indicate that data is present */ if (list_empty(&ps->msg_queue)) { uint8_t ch = '\0'; @@ -3806,7 +3796,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, } } list_add_tail(&msg->link, &ps->msg_queue); - pthread_mutex_unlock(&ps->mutex); + js_mutex_unlock(&ps->mutex); return JS_UNDEFINED; fail: if (msg) { diff --git a/run-test262.c b/run-test262.c index c1717fb51..1b109ca95 100644 --- a/run-test262.c +++ b/run-test262.c @@ -35,12 +35,9 @@ #ifdef _WIN32 #include #include -typedef HANDLE js_thread_t; #else #include -#include #include -typedef pthread_t js_thread_t; #endif #include "cutils.h" @@ -530,49 +527,6 @@ static JSValue js_evalScript_262(JSContext *ctx, JSValueConst this_val, return ret; } -static void start_thread(js_thread_t *thrd, void *(*start)(void *), void *arg) -{ - // musl libc gives threads 80 kb stacks, much smaller than - // JS_DEFAULT_STACK_SIZE (1 MB) - static const unsigned stacksize = 2 << 20; // 2 MB, glibc default -#ifdef _WIN32 - HANDLE h, cp; - - cp = GetCurrentProcess(); - h = (HANDLE)_beginthread((void (*)(void *))(void *)start, stacksize, arg); - if (!h) - fatal(1, "_beginthread error"); - // _endthread() automatically closes the handle but we want to wait on - // it so make a copy. Race-y for very short-lived threads. Can be solved - // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing - // |start| from __cdecl to __stdcall. - if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS)) - fatal(1, "DuplicateHandle error"); -#else - pthread_attr_t attr; - - if (pthread_attr_init(&attr)) - fatal(1, "pthread_attr_init"); - if (pthread_attr_setstacksize(&attr, stacksize)) - fatal(1, "pthread_attr_setstacksize"); - if (pthread_create(thrd, &attr, start, arg)) - fatal(1, "pthread_create error"); - pthread_attr_destroy(&attr); -#endif -} - -static void join_thread(js_thread_t thrd) -{ -#ifdef _WIN32 - if (WaitForSingleObject(thrd, INFINITE)) - fatal(1, "WaitForSingleObject error"); - CloseHandle(thrd); -#else - if (pthread_join(thrd, NULL)) - fatal(1, "pthread_join error"); -#endif -} - static long cpu_count(void) { #ifdef _WIN32 @@ -621,7 +575,7 @@ typedef struct { static JSValue add_helpers1(JSContext *ctx); static void add_helpers(JSContext *ctx); -static void *agent_start(void *arg) +static void agent_start(void *arg) { ThreadLocalStorage *tls; Test262Agent *agent; @@ -697,7 +651,6 @@ static void *agent_start(void *arg) JS_FreeContext(ctx); JS_FreeRuntime(rt); - return NULL; } static JSValue js_agent_start(JSContext *ctx, JSValueConst this_val, @@ -721,7 +674,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValueConst this_val, agent->tls = tls; JS_FreeCString(ctx, script); list_add_tail(&agent->link, &tls->agent_list); - start_thread(&agent->tid, agent_start, agent); + js_thread_create(&agent->tid, agent_start, agent, /*flags*/0); return JS_UNDEFINED; } @@ -733,7 +686,7 @@ static void js_agent_free(JSContext *ctx) list_for_each_safe(el, el1, &tls->agent_list) { agent = list_entry(el, Test262Agent, link); - join_thread(agent->tid); + js_thread_join(agent->tid); JS_FreeValue(ctx, agent->broadcast_sab); list_del(&agent->link); free(agent); @@ -2083,7 +2036,7 @@ int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename, clock_t last_clock; -void *show_progress(void *unused) { +void show_progress(void *unused) { int interval = 1000*1000*1000 / 4; // 250 ms js_mutex_lock(&progress_mutex); @@ -2096,7 +2049,6 @@ void *show_progress(void *unused) { fflush(stderr); } js_mutex_unlock(&progress_mutex); - return NULL; } enum { INCLUDE, EXCLUDE, SKIP }; @@ -2112,7 +2064,7 @@ int include_exclude_or_skip(int i) // naming is hard... return INCLUDE; } -void *run_test_dir_list(void *arg) +void run_test_dir_list(void *arg) { ThreadLocalStorage tls_s, *tls = &tls_s; const char *p; @@ -2129,7 +2081,6 @@ void *run_test_dir_list(void *arg) if (verbose > 1 || (slow_test_threshold && msec >= slow_test_threshold)) fprintf(stderr, "%s (%d ms)\n", p, msec); } - return NULL; } void help(void) @@ -2304,15 +2255,17 @@ int main(int argc, char **argv) } js_cond_init(&progress_cond); js_mutex_init(&progress_mutex); - start_thread(&progress_thread, show_progress, NULL); - for (i = 0; i < nthreads; i++) - start_thread(&threads[i], run_test_dir_list, (void *)(uintptr_t)i); + js_thread_create(&progress_thread, show_progress, NULL, /*flags*/0); + for (i = 0; i < nthreads; i++) { + js_thread_create(&threads[i], run_test_dir_list, + (void *)(uintptr_t)i, /*flags*/0); + } for (i = 0; i < nthreads; i++) - join_thread(threads[i]); + js_thread_join(threads[i]); js_mutex_lock(&progress_mutex); js_cond_signal(&progress_cond); js_mutex_unlock(&progress_mutex); - join_thread(progress_thread); + js_thread_join(progress_thread); js_mutex_destroy(&progress_mutex); js_cond_destroy(&progress_cond); } else { diff --git a/tests/test_worker.js b/tests/test_worker.js index 8309c1891..d1660ce54 100644 --- a/tests/test_worker.js +++ b/tests/test_worker.js @@ -40,6 +40,4 @@ function test_worker() }; } -if (os.platform !== 'win32') { - test_worker(); -} +test_worker(); From 355ec4269a28310e639bbf6fc0351085807fafff Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 14:16:31 +0200 Subject: [PATCH 02/18] fixup! match cutils.h guard --- cutils.h | 8 +++++++- quickjs-libc.c | 13 ++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cutils.h b/cutils.h index bf95a30c5..b15328765 100644 --- a/cutils.h +++ b/cutils.h @@ -563,7 +563,13 @@ static inline size_t js__malloc_usable_size(const void *ptr) /* Cross-platform threading APIs. */ -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#if defined(EMSCRIPTEN) || defined(__wasi__) + +#define JS_HAVE_THREADS 0 + +#else + +#define JS_HAVE_THREADS 1 #if defined(_WIN32) #define JS_ONCE_INIT INIT_ONCE_STATIC_INIT diff --git a/quickjs-libc.c b/quickjs-libc.c index 4a469ee62..ce04db3d9 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -80,19 +80,18 @@ extern char **environ; #endif /* _WIN32 */ -#if !defined(__wasi__) -/* enable the os.Worker API. IT relies on POSIX threads */ -#define USE_WORKER +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +#if JS_HAVE_THREADS +#define USE_WORKER // enable os.Worker #endif #ifdef USE_WORKER #include "quickjs-c-atomics.h" #endif -#include "cutils.h" -#include "list.h" -#include "quickjs-libc.h" - #ifndef MAX_SAFE_INTEGER // already defined in amalgamation builds #define MAX_SAFE_INTEGER (((int64_t) 1 << 53) - 1) #endif From 84c3424068211afa913b79c3d882f32eb42844a8 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 14:21:00 +0200 Subject: [PATCH 03/18] fixup! fix guards in cutils.c --- cutils.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cutils.c b/cutils.c index 3a23eb0e1..37a4383c0 100644 --- a/cutils.c +++ b/cutils.c @@ -1199,8 +1199,7 @@ int64_t js__gettimeofday_us(void) { /*--- Cross-platform threading APIs. ----*/ -#if !defined(EMSCRIPTEN) && !defined(__wasi__) - +#if JS_HAVE_THREADS #if defined(_WIN32) typedef void (*js__once_cb)(void); @@ -1437,8 +1436,6 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { return -1; } -#endif - int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, int flags) { @@ -1474,7 +1471,8 @@ int js_thread_join(js_thread_t thrd) return 0; } -#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ +#endif /* !defined(_WIN32) */ +#endif /* JS_HAVE_THREADS */ #ifdef __GNUC__ #pragma GCC visibility pop From 13735bb6c4efea20083f61035ebb7e6ce92b548c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 14:36:46 +0200 Subject: [PATCH 04/18] fixup! add atomic_fetch_add --- quickjs-c-atomics.h | 8 ++++++++ quickjs-libc.c | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/quickjs-c-atomics.h b/quickjs-c-atomics.h index 8fc6b7203..5a8e9dec3 100644 --- a/quickjs-c-atomics.h +++ b/quickjs-c-atomics.h @@ -49,6 +49,14 @@ #define atomic_fetch_sub(obj, arg) \ __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) #define _Atomic +#elif defined(_MSC_VER) +#include +#include +#define _Atomic volatile +static inline int atomic_fetch_add(_Atomic int *p, int v) +{ + return InterlockedExchangeAdd(p, v); +} #else #include #endif diff --git a/quickjs-libc.c b/quickjs-libc.c index ce04db3d9..4357f0d62 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -139,7 +139,7 @@ typedef struct { } JSWorkerMessage; typedef struct { - int ref_count; + _Atomic int ref_count; #ifdef USE_WORKER js_mutex_t mutex; #endif @@ -3440,15 +3440,15 @@ typedef struct { } WorkerFuncArgs; typedef struct { - int ref_count; + _Atomic int ref_count; uint64_t buf[]; } JSSABHeader; static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); -static int atomic_add_int(int *ptr, int v) +static int atomic_add_int(_Atomic int *ptr, int v) { - return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; + return atomic_fetch_add(ptr, v) + v; } /* shared array buffer allocator */ From 50a8b2dd860e03580f4a031374dd78781fba895c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 15:10:48 +0200 Subject: [PATCH 05/18] fixup! include process.h --- cutils.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cutils.c b/cutils.c index 37a4383c0..cf4e9e423 100644 --- a/cutils.c +++ b/cutils.c @@ -31,6 +31,9 @@ #if !defined(_MSC_VER) #include #endif +#if defined(_WIN32) +#include // _beginthread +#endif #include "cutils.h" From 028c40881c0e5f788d7732a8ce877f439ec6d7f2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 15:12:22 +0200 Subject: [PATCH 06/18] Revert "fixup! add atomic_fetch_add" This reverts commit 13735bb6c4efea20083f61035ebb7e6ce92b548c. --- quickjs-c-atomics.h | 8 -------- quickjs-libc.c | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/quickjs-c-atomics.h b/quickjs-c-atomics.h index 5a8e9dec3..8fc6b7203 100644 --- a/quickjs-c-atomics.h +++ b/quickjs-c-atomics.h @@ -49,14 +49,6 @@ #define atomic_fetch_sub(obj, arg) \ __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) #define _Atomic -#elif defined(_MSC_VER) -#include -#include -#define _Atomic volatile -static inline int atomic_fetch_add(_Atomic int *p, int v) -{ - return InterlockedExchangeAdd(p, v); -} #else #include #endif diff --git a/quickjs-libc.c b/quickjs-libc.c index 4357f0d62..ce04db3d9 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -139,7 +139,7 @@ typedef struct { } JSWorkerMessage; typedef struct { - _Atomic int ref_count; + int ref_count; #ifdef USE_WORKER js_mutex_t mutex; #endif @@ -3440,15 +3440,15 @@ typedef struct { } WorkerFuncArgs; typedef struct { - _Atomic int ref_count; + int ref_count; uint64_t buf[]; } JSSABHeader; static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); -static int atomic_add_int(_Atomic int *ptr, int v) +static int atomic_add_int(int *ptr, int v) { - return atomic_fetch_add(ptr, v) + v; + return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; } /* shared array buffer allocator */ From df08b4142de69fd374221914c7a90fe84399314c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 17:09:19 +0200 Subject: [PATCH 07/18] fixup! implement JSWorkerMessagePipe --- cutils.c | 1 + quickjs-libc.c | 173 ++++++++++++++++++++++++++++++------------------- 2 files changed, 109 insertions(+), 65 deletions(-) diff --git a/cutils.c b/cutils.c index cf4e9e423..0e70f69e2 100644 --- a/cutils.c +++ b/cutils.c @@ -32,6 +32,7 @@ #include #endif #if defined(_WIN32) +#include #include // _beginthread #endif diff --git a/quickjs-libc.c b/quickjs-libc.c index ce04db3d9..d3c6de93f 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -85,11 +85,8 @@ extern char **environ; #include "quickjs-libc.h" #if JS_HAVE_THREADS -#define USE_WORKER // enable os.Worker -#endif - -#ifdef USE_WORKER #include "quickjs-c-atomics.h" +#define USE_WORKER // enable os.Worker #endif #ifndef MAX_SAFE_INTEGER // already defined in amalgamation builds @@ -144,8 +141,12 @@ typedef struct { js_mutex_t mutex; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ +#ifdef _WIN32 + HANDLE event_handle; +#else int read_fd; int write_fd; +#endif } JSWorkerMessagePipe; typedef struct { @@ -2391,62 +2392,7 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } -#if defined(_WIN32) - -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - int min_delay, console_fd; - JSOSRWHandler *rh; - struct list_head *el; - - /* XXX: handle signals if useful */ - - if (js_os_run_timers(rt, ctx, ts, &min_delay)) - return -1; - if (min_delay == 0) - return 0; // expired timer - if (min_delay < 0) - if (list_empty(&ts->os_rw_handlers)) - return -1; /* no more events */ - - console_fd = -1; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - console_fd = rh->fd; - break; - } - } - - if (console_fd >= 0) { - DWORD ti, ret; - HANDLE handle; - if (min_delay == -1) - ti = INFINITE; - else - ti = min_delay; - handle = (HANDLE)_get_osfhandle(console_fd); - ret = WaitForSingleObject(handle, ti); - if (ret == WAIT_OBJECT_0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { - return call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - } - } - } - } else { - Sleep(min_delay); - } - return 0; -} -#else - #ifdef USE_WORKER - static void js_free_message(JSWorkerMessage *msg); /* return 1 if a message was handled, 0 if no message */ @@ -2467,6 +2413,8 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, /* remove the message from the queue */ list_del(&msg->link); +#ifndef _WIN32 + // drain read end of pipe if (list_empty(&ps->msg_queue)) { uint8_t buf[16]; int ret; @@ -2478,6 +2426,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, break; } } +#endif // !defined(_WIN32) js_mutex_unlock(&ps->mutex); @@ -2514,14 +2463,80 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } return ret; } -#else -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) +#endif // USE_WORKER + +#if defined(_WIN32) +static int js_os_poll(JSContext *ctx) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + int min_delay, count, rwcount; + JSOSRWHandler *rh; + struct list_head *el; + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 + + /* XXX: handle signals if useful */ + + if (js_os_run_timers(rt, ctx, ts, &min_delay)) + return -1; + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers)) + return -1; /* no more events */ + + count = 0; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) + handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin + if (count == (int)countof(handles)) + break; + } + rwcount = count; + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (JS_IsNull(port->on_message_func)) + continue; + handles[count++] = port->recv_pipe->event_handle; + if (count == (int)countof(handles)) + break; + } + + if (count > 0) { + DWORD ti, ret; + if (min_delay == -1) + ti = INFINITE; + else + ti = min_delay; + ret = WaitForMultipleObjects(count, handles, FALSE, ti); + if (ret < WAIT_OBJECT_0+rwcount) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + return call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + } + } + } else if (ret < WAIT_OBJECT_0+count) { + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (JS_IsNull(port->on_message_func)) + continue; + if (port->recv_pipe->event_handle == handles[ret]) { + if (handle_posted_message(rt, ctx, port)) + return 0; + /* must stop because the list may have been modified */ + } + } + } + } else { + Sleep(min_delay); + } return 0; } -#endif - +#else // !defined(_WIN32) static int js_os_poll(JSContext *ctx) { JSRuntime *rt = JS_GetRuntime(ctx); @@ -2614,7 +2629,8 @@ static int js_os_poll(JSContext *ctx) done: return 0; } -#endif /* !_WIN32 */ +#endif // defined(_WIN32) + static JSValue make_obj_error(JSContext *ctx, JSValue obj, @@ -3483,6 +3499,24 @@ static void js_sab_dup(void *opaque, void *ptr) static JSWorkerMessagePipe *js_new_message_pipe(void) { +#ifdef _WIN32 + JSWorkerMessagePipe *ps; + HANDLE event_handle; + + event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!event_handle) + return NULL; + ps = malloc(sizeof(*ps)); + if (!ps) { + CloseHandle(event_handle); + return NULL; + } + ps->ref_count = 1; + init_list_head(&ps->msg_queue); + js_mutex_init(&ps->mutex); + ps->event_handle = event_handle; + return ps; +#else // !defined(_WIN32) JSWorkerMessagePipe *ps; int pipe_fds[2]; @@ -3501,6 +3535,7 @@ static JSWorkerMessagePipe *js_new_message_pipe(void) ps->read_fd = pipe_fds[0]; ps->write_fd = pipe_fds[1]; return ps; +#endif // defined(_WIN32) } static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) @@ -3538,8 +3573,12 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) js_free_message(msg); } js_mutex_destroy(&ps->mutex); +#ifdef _WIN32 + CloseHandle(ps->event_handle); +#else close(ps->read_fd); close(ps->write_fd); +#endif free(ps); } } @@ -3784,6 +3823,9 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, js_mutex_lock(&ps->mutex); /* indicate that data is present */ if (list_empty(&ps->msg_queue)) { +#ifdef _WIN32 + SetEvent(ps->event_handle); +#else // !defined(_WIN32) uint8_t ch = '\0'; int ret; for(;;) { @@ -3793,6 +3835,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, if (ret < 0 && (errno != EAGAIN || errno != EINTR)) break; } +#endif // defined(_WIN32) } list_add_tail(&msg->link, &ps->msg_queue); js_mutex_unlock(&ps->mutex); From a458bc9c3add62371c57d7eb89009d8c4a35c986 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 17:14:45 +0200 Subject: [PATCH 08/18] fixup! fix wasi, bring back stub handle_posted_message --- quickjs-libc.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index d3c6de93f..e9f558347 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2392,9 +2392,19 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } -#ifdef USE_WORKER -static void js_free_message(JSWorkerMessage *msg); +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} +#ifdef USE_WORKER /* return 1 if a message was handled, 0 if no message */ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JSWorkerMessageHandler *port) @@ -2463,6 +2473,12 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } return ret; } +#else // !USE_WORKER +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + return 0; +} #endif // USE_WORKER #if defined(_WIN32) @@ -3544,18 +3560,6 @@ static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) return ps; } -static void js_free_message(JSWorkerMessage *msg) -{ - size_t i; - /* free the SAB */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_free(NULL, msg->sab_tab[i]); - } - free(msg->sab_tab); - free(msg->data); - free(msg); -} - static void js_free_message_pipe(JSWorkerMessagePipe *ps) { struct list_head *el, *el1; From daea2dae3a02169b37a5bf6407794fcd81241cb5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 17:17:24 +0200 Subject: [PATCH 09/18] fixup! fix wasi, bring back stub handle_posted_message --- quickjs-libc.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index e9f558347..acab769c7 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2392,17 +2392,7 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } -static void js_free_message(JSWorkerMessage *msg) -{ - size_t i; - /* free the SAB */ - for(i = 0; i < msg->sab_tab_len; i++) { - js_sab_free(NULL, msg->sab_tab[i]); - } - free(msg->sab_tab); - free(msg->data); - free(msg); -} +static void js_free_message(JSWorkerMessage *msg); #ifdef USE_WORKER /* return 1 if a message was handled, 0 if no message */ @@ -3560,6 +3550,18 @@ static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) return ps; } +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} + static void js_free_message_pipe(JSWorkerMessagePipe *ps) { struct list_head *el, *el1; From e91ac8a2366e70211f19af8baf1821929d6ddbbb Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 21:41:03 +0200 Subject: [PATCH 10/18] fixup! fix unused function warning --- quickjs-libc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index acab769c7..eec24af83 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2392,9 +2392,9 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } +#ifdef USE_WORKER static void js_free_message(JSWorkerMessage *msg); -#ifdef USE_WORKER /* return 1 if a message was handled, 0 if no message */ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JSWorkerMessageHandler *port) From 4dc62ddfca25de8aa42f10493814b8b28cf55eeb Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 21:59:22 +0200 Subject: [PATCH 11/18] fixup! fix cmake mingw detection --- CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 133de63f5..5d7d19511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,17 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_STANDARD 11) +# MINGW doesn't exist in older cmake versions, newer versions don't know +# about CMAKE_COMPILER_IS_MINGW, and there is no unique CMAKE_C_COMPILER_ID +# for mingw-based compilers... +if(MINGW) + # do nothing +elseif(CMAKE_C_COMPILER MATCHES "mingw") + set(MINGW TRUE) +else() + set(MINGW FALSE) +endif() + if(NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Release") set(CMAKE_BUILD_TYPE "Release") From 7323cc19fb55d1623d8dc431660aff055512ef69 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 23:24:57 +0200 Subject: [PATCH 12/18] fixup! windows worker <-> parent ipc --- cutils.c | 11 +-- quickjs-libc.c | 199 +++++++++++++++++++++++++++---------------------- run-test262.c | 2 + 3 files changed, 118 insertions(+), 94 deletions(-) diff --git a/cutils.c b/cutils.c index 0e70f69e2..a9efdcbd6 100644 --- a/cutils.c +++ b/cutils.c @@ -1278,18 +1278,19 @@ int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg, *thrd = INVALID_HANDLE_VALUE; if (flags & ~JS_THREAD_CREATE_DETACHED) return -1; - cp = GetCurrentProcess(); h = (HANDLE)_beginthread(start, /*stacksize*/2<<20, arg); if (!h) return -1; + if (flags & JS_THREAD_CREATE_DETACHED) + return 0; // _endthread() automatically closes the handle but we want to wait on // it so make a copy. Race-y for very short-lived threads. Can be solved // by switching to _beginthreadex(CREATE_SUSPENDED) but means changing // |start| from __cdecl to __stdcall. - if (!(flags & JS_THREAD_CREATE_DETACHED)) - if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS)) - return -1; - return 0; + cp = GetCurrentProcess(); + if (DuplicateHandle(cp, h, cp, thrd, 0, FALSE, DUPLICATE_SAME_ACCESS)) + return 0; + return -1; } int js_thread_join(js_thread_t thrd) diff --git a/quickjs-libc.c b/quickjs-libc.c index eec24af83..d8f71e535 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -135,18 +135,22 @@ typedef struct { size_t sab_tab_len; } JSWorkerMessage; +typedef struct JSWaker { +#ifdef _WIN32 + HANDLE handle; +#else + int read_fd; + int write_fd; +#endif +} JSWaker; + typedef struct { int ref_count; #ifdef USE_WORKER js_mutex_t mutex; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ -#ifdef _WIN32 - HANDLE event_handle; -#else - int read_fd; - int write_fd; -#endif + JSWaker waker; } JSWorkerMessagePipe; typedef struct { @@ -2392,6 +2396,78 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } +#ifdef _WIN32 + +static int js_waker_init(JSWaker *w) +{ + w->handle = CreateEvent(NULL, TRUE, FALSE, NULL); + return w->handle ? 0 : -1; +} + +static void js_waker_signal(JSWaker *w) +{ + SetEvent(w->handle); +} + +static void js_waker_clear(JSWaker *w) +{ + ResetEvent(w->handle); +} + +static void js_waker_close(JSWaker *w) +{ + CloseHandle(w->handle); + w->handle = INVALID_HANDLE_VALUE; +} + +#else // !_WIN32 + +static int js_waker_init(JSWaker *w) +{ + int fds[2]; + + if (pipe(fds) < 0) + return -1; + w->read_fd = fds[0]; + w->write_fd = fds[1]; + return 0; +} + +static void js_waker_signal(JSWaker *w) +{ + int ret; + + for(;;) { + ret = write(w->write_fd, "", 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) + break; + } +} + +static void js_waker_clear(JSWaker *w) +{ + uint8_t buf[16]; + int ret; + + for(;;) { + ret = read(w->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + } +} + +static void js_waker_close(JSWaker *w) +{ + close(w->read_fd); + close(w->write_fd); +} + +#endif // _WIN32 + #ifdef USE_WORKER static void js_free_message(JSWorkerMessage *msg); @@ -2413,20 +2489,9 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, /* remove the message from the queue */ list_del(&msg->link); -#ifndef _WIN32 // drain read end of pipe - if (list_empty(&ps->msg_queue)) { - uint8_t buf[16]; - int ret; - for(;;) { - ret = read(ps->read_fd, buf, sizeof(buf)); - if (ret >= 0) - break; - if (errno != EAGAIN && errno != EINTR) - break; - } - } -#endif // !defined(_WIN32) + if (list_empty(&ps->msg_queue)) + js_waker_clear(&ps->waker); js_mutex_unlock(&ps->mutex); @@ -2476,7 +2541,7 @@ static int js_os_poll(JSContext *ctx) { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = js_get_thread_state(rt); - int min_delay, count, rwcount; + int min_delay, count; JSOSRWHandler *rh; struct list_head *el; HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64 @@ -2488,7 +2553,7 @@ static int js_os_poll(JSContext *ctx) if (min_delay == 0) return 0; // expired timer if (min_delay < 0) - if (list_empty(&ts->os_rw_handlers)) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) return -1; /* no more events */ count = 0; @@ -2499,25 +2564,22 @@ static int js_os_poll(JSContext *ctx) if (count == (int)countof(handles)) break; } - rwcount = count; list_for_each(el, &ts->port_list) { JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (JS_IsNull(port->on_message_func)) continue; - handles[count++] = port->recv_pipe->event_handle; + handles[count++] = port->recv_pipe->waker.handle; if (count == (int)countof(handles)) break; } if (count > 0) { - DWORD ti, ret; - if (min_delay == -1) - ti = INFINITE; - else - ti = min_delay; - ret = WaitForMultipleObjects(count, handles, FALSE, ti); - if (ret < WAIT_OBJECT_0+rwcount) { + DWORD ret, timeout = INFINITE; + if (min_delay != -1) + timeout = min_delay; + ret = WaitForMultipleObjects(count, handles, FALSE, timeout); + if (ret < count) { list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { @@ -2525,21 +2587,22 @@ static int js_os_poll(JSContext *ctx) /* must stop because the list may have been modified */ } } - } else if (ret < WAIT_OBJECT_0+count) { + list_for_each(el, &ts->port_list) { JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); - if (JS_IsNull(port->on_message_func)) - continue; - if (port->recv_pipe->event_handle == handles[ret]) { - if (handle_posted_message(rt, ctx, port)) - return 0; - /* must stop because the list may have been modified */ + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (ps->waker.handle == handles[ret]) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } } } } } else { Sleep(min_delay); } +done: return 0; } #else // !defined(_WIN32) @@ -2600,8 +2663,8 @@ static int js_os_poll(JSContext *ctx) JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { JSWorkerMessagePipe *ps = port->recv_pipe; - fd_max = max_int(fd_max, ps->read_fd); - FD_SET(ps->read_fd, &rfds); + fd_max = max_int(fd_max, ps->waker.read_fd); + FD_SET(ps->waker.read_fd, &rfds); } } @@ -2625,14 +2688,14 @@ static int js_os_poll(JSContext *ctx) JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { JSWorkerMessagePipe *ps = port->recv_pipe; - if (FD_ISSET(ps->read_fd, &rfds)) { + if (FD_ISSET(ps->waker.read_fd, &rfds)) { if (handle_posted_message(rt, ctx, port)) goto done; } } } } - done: +done: return 0; } #endif // defined(_WIN32) @@ -3505,43 +3568,19 @@ static void js_sab_dup(void *opaque, void *ptr) static JSWorkerMessagePipe *js_new_message_pipe(void) { -#ifdef _WIN32 JSWorkerMessagePipe *ps; - HANDLE event_handle; - event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!event_handle) - return NULL; ps = malloc(sizeof(*ps)); - if (!ps) { - CloseHandle(event_handle); - return NULL; - } - ps->ref_count = 1; - init_list_head(&ps->msg_queue); - js_mutex_init(&ps->mutex); - ps->event_handle = event_handle; - return ps; -#else // !defined(_WIN32) - JSWorkerMessagePipe *ps; - int pipe_fds[2]; - - if (pipe(pipe_fds) < 0) + if (!ps) return NULL; - - ps = malloc(sizeof(*ps)); - if (!ps) { - close(pipe_fds[0]); - close(pipe_fds[1]); + if (js_waker_init(&ps->waker)) { + free(ps); return NULL; } ps->ref_count = 1; init_list_head(&ps->msg_queue); js_mutex_init(&ps->mutex); - ps->read_fd = pipe_fds[0]; - ps->write_fd = pipe_fds[1]; return ps; -#endif // defined(_WIN32) } static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) @@ -3579,12 +3618,7 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps) js_free_message(msg); } js_mutex_destroy(&ps->mutex); -#ifdef _WIN32 - CloseHandle(ps->event_handle); -#else - close(ps->read_fd); - close(ps->write_fd); -#endif + js_waker_close(&ps->waker); free(ps); } } @@ -3828,21 +3862,8 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, ps = worker->send_pipe; js_mutex_lock(&ps->mutex); /* indicate that data is present */ - if (list_empty(&ps->msg_queue)) { -#ifdef _WIN32 - SetEvent(ps->event_handle); -#else // !defined(_WIN32) - uint8_t ch = '\0'; - int ret; - for(;;) { - ret = write(ps->write_fd, &ch, 1); - if (ret == 1) - break; - if (ret < 0 && (errno != EAGAIN || errno != EINTR)) - break; - } -#endif // defined(_WIN32) - } + if (list_empty(&ps->msg_queue)) + js_waker_signal(&ps->waker); list_add_tail(&msg->link, &ps->msg_queue); js_mutex_unlock(&ps->mutex); return JS_UNDEFINED; diff --git a/run-test262.c b/run-test262.c index 1b109ca95..03afe82df 100644 --- a/run-test262.c +++ b/run-test262.c @@ -1726,6 +1726,7 @@ int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness, if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } + JS_SetDumpFlags(rt, JS_DUMP_LEAKS); JS_SetRuntimeOpaque(rt, tls); js_std_init_handlers(rt); ctx = JS_NewCustomContext(rt); @@ -1971,6 +1972,7 @@ int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename, if (rt == NULL) { fatal(1, "JS_NewRuntime failure"); } + JS_SetDumpFlags(rt, JS_DUMP_LEAKS); JS_SetRuntimeOpaque(rt, tls); ctx = JS_NewContext(rt); if (ctx == NULL) { From 4c25d7db4af0893b79aea1571c386deaa598cc06 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 23:30:37 +0200 Subject: [PATCH 13/18] fixup! wasi again... --- quickjs-libc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index d8f71e535..e48a7aa02 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -148,9 +148,9 @@ typedef struct { int ref_count; #ifdef USE_WORKER js_mutex_t mutex; + JSWaker waker; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ - JSWaker waker; } JSWorkerMessagePipe; typedef struct { @@ -2396,6 +2396,8 @@ static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts, in return 0; } +#ifdef USE_WORKER + #ifdef _WIN32 static int js_waker_init(JSWaker *w) @@ -2468,7 +2470,6 @@ static void js_waker_close(JSWaker *w) #endif // _WIN32 -#ifdef USE_WORKER static void js_free_message(JSWorkerMessage *msg); /* return 1 if a message was handled, 0 if no message */ @@ -2528,12 +2529,15 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, } return ret; } + #else // !USE_WORKER + static int handle_posted_message(JSRuntime *rt, JSContext *ctx, JSWorkerMessageHandler *port) { return 0; } + #endif // USE_WORKER #if defined(_WIN32) From bac261c2466faaa1f50c3d81fc263ec0bb4c634b Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 23:31:55 +0200 Subject: [PATCH 14/18] fixup! remove windows-msvc-vs2019 buildbots, no stdatomic.h --- .github/workflows/ci.yml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b174f0144..901d4dd8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -209,31 +209,6 @@ jobs: cl.exe /DJS_NAN_BOXING=0 /Zs cxxtest.cc cl.exe /DJS_NAN_BOXING=1 /Zs cxxtest.cc - windows-msvc-vs2019: - runs-on: windows-2019 - strategy: - fail-fast: false - matrix: - arch: [x64, Win32] - buildType: [Debug, Release] - steps: - - uses: actions/checkout@v4 - - name: build - run: | - cmake -B build -DQJS_BUILD_EXAMPLES=ON -G "Visual Studio 16 2019" -A ${{matrix.arch}} - cmake --build build --config ${{matrix.buildType}} --target qjs_exe - - name: stats - run: | - build\${{matrix.buildType}}\qjs.exe -qd - - name: Set up Visual Studio shell - uses: egor-tensin/vs-shell@v2 - with: - arch: ${{ matrix.arch == 'x64' && 'x64' || 'x86' }} - - name: cxxtest - run: | - cl.exe /DJS_NAN_BOXING=0 /Zs cxxtest.cc - cl.exe /DJS_NAN_BOXING=1 /Zs cxxtest.cc - windows-clang: runs-on: windows-latest strategy: From ee3dd0e4ab1824c7e260e7c4f2846a771ccbddf4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 30 Mar 2025 23:39:26 +0200 Subject: [PATCH 15/18] fixup! fu wasi --- quickjs-libc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index e48a7aa02..bd5d4b47d 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -148,9 +148,9 @@ typedef struct { int ref_count; #ifdef USE_WORKER js_mutex_t mutex; - JSWaker waker; #endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ + JSWaker waker; } JSWorkerMessagePipe; typedef struct { From e844772d69f8576b705d3de08ada5c69d1ea0310 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 31 Mar 2025 10:53:43 +0200 Subject: [PATCH 16/18] squash! review feedback --- docs/docs/supported_platforms.md | 2 +- quickjs-libc.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/docs/supported_platforms.md b/docs/docs/supported_platforms.md index 94149ed9c..bb817b3d4 100644 --- a/docs/docs/supported_platforms.md +++ b/docs/docs/supported_platforms.md @@ -8,7 +8,7 @@ sidebar_position: 8 |---|---|---| | GNU/Linux | * | glibc and musl are supported | | macOS | macOS >= 11 | Currently supported macOS releases | -| Windows | >= Windows 7* | VS >= 2022 and Clang are supported | +| Windows | >= Windows 7* | VS >= 2022 and Clang are supported; requires `` | | FreeBSD | * | Limited testing | | OpenBSD | * | Limited testing | | NetBSD | * | Limited testing | diff --git a/quickjs-libc.c b/quickjs-libc.c index bd5d4b47d..e7be476c3 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2466,6 +2466,8 @@ static void js_waker_close(JSWaker *w) { close(w->read_fd); close(w->write_fd); + w->read_fd = -1; + w->write_fd = -1; } #endif // _WIN32 From a689d9e67ea73dc40a2ac935bb8f61a2bfd2a8c5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 31 Mar 2025 11:03:28 +0200 Subject: [PATCH 17/18] squash! review feedback --- quickjs-libc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index e7be476c3..ac2c63e92 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -126,6 +126,8 @@ typedef struct { JSValue func; } JSOSTimer; +#ifdef USE_WORKER + typedef struct { struct list_head link; uint8_t *data; @@ -146,9 +148,7 @@ typedef struct JSWaker { typedef struct { int ref_count; -#ifdef USE_WORKER js_mutex_t mutex; -#endif struct list_head msg_queue; /* list of JSWorkerMessage.link */ JSWaker waker; } JSWorkerMessagePipe; @@ -159,6 +159,8 @@ typedef struct { JSValue on_message_func; } JSWorkerMessageHandler; +#endif // USE_WORKER + typedef struct JSThreadState { struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ @@ -168,7 +170,11 @@ typedef struct JSThreadState { int64_t next_timer_id; /* for setTimeout / setInterval */ bool can_js_os_poll; /* not used in the main thread */ +#ifdef USE_WORKER JSWorkerMessagePipe *recv_pipe, *send_pipe; +#else + void *recv_pipe; +#endif // USE_WORKER JSClassID std_file_class_id; JSClassID worker_class_id; } JSThreadState; @@ -2532,14 +2538,6 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx, return ret; } -#else // !USE_WORKER - -static int handle_posted_message(JSRuntime *rt, JSContext *ctx, - JSWorkerMessageHandler *port) -{ - return 0; -} - #endif // USE_WORKER #if defined(_WIN32) @@ -2665,6 +2663,7 @@ static int js_os_poll(JSContext *ctx) FD_SET(rh->fd, &wfds); } +#ifdef USE_WORKER list_for_each(el, &ts->port_list) { JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { @@ -2673,6 +2672,7 @@ static int js_os_poll(JSContext *ctx) FD_SET(ps->waker.read_fd, &rfds); } } +#endif // USE_WORKER ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); if (ret > 0) { @@ -2689,7 +2689,7 @@ static int js_os_poll(JSContext *ctx) /* must stop because the list may have been modified */ } } - +#ifdef USE_WORKER list_for_each(el, &ts->port_list) { JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); if (!JS_IsNull(port->on_message_func)) { @@ -2700,6 +2700,7 @@ static int js_os_poll(JSContext *ctx) } } } +#endif // USE_WORKER } done: return 0; From 195c5110484f337c8f8e35b42a57201f0ac5a7b0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 31 Mar 2025 11:16:50 +0200 Subject: [PATCH 18/18] fixup! silence unused label warning --- quickjs-libc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/quickjs-libc.c b/quickjs-libc.c index ac2c63e92..c87c04fbd 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -2702,6 +2702,7 @@ static int js_os_poll(JSContext *ctx) } #endif // USE_WORKER } + goto done; // silence unused label warning done: return 0; }