From aca5428846cb961b8a443f6b36d3cdba70487fd6 Mon Sep 17 00:00:00 2001 From: Michael Camilleri Date: Tue, 16 Sep 2025 10:24:24 +0900 Subject: [PATCH 1/4] Use SetEvent rather than QueueUserAPC on Windows --- src/core/ev.c | 31 +++++++++++++++++++++++++------ src/core/state.h | 1 + 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/core/ev.c b/src/core/ev.c index 3acf9dd6..8422168a 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -117,6 +117,9 @@ typedef struct { double sec; JanetVM *vm; JanetFiber *fiber; +#ifdef JANET_WINDOWS + HANDLE cancel_event; +#endif } JanetThreadedTimeout; #define JANET_MAX_Q_CAPACITY 0x7FFFFFF @@ -620,10 +623,14 @@ static void janet_timeout_stop(int sig_num) { static void handle_timeout_worker(JanetTimeout to, int cancel) { if (!to.has_worker) return; #ifdef JANET_WINDOWS - (void) cancel; - QueueUserAPC(janet_timeout_stop, to.worker, 0); + if (cancel && to.worker_event) { + SetEvent(to.worker_event); + } WaitForSingleObject(to.worker, INFINITE); CloseHandle(to.worker); + if (to.worker_event) { + CloseHandle(to.worker_event); + } #else #ifdef JANET_ANDROID if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill"); @@ -693,10 +700,13 @@ static void janet_timeout_cb(JanetEVGenericMessage msg) { static DWORD WINAPI janet_timeout_body(LPVOID ptr) { JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr; janet_free(ptr); - SleepEx((DWORD)(tto.sec * 1000), TRUE); - janet_interpreter_interrupt(tto.vm); - JanetEVGenericMessage msg = {0}; - janet_ev_post_event(tto.vm, janet_timeout_cb, msg); + DWORD res = WaitForSingleObject(tto.cancel_event, (DWORD)(tto.sec * 1000)); + /* only send interrupt message if result is WAIT_TIMEOUT */ + if (res == WAIT_TIMEOUT) { + janet_interpreter_interrupt(tto.vm); + JanetEVGenericMessage msg = {0}; + janet_ev_post_event(tto.vm, janet_timeout_cb, msg); + } return 0; } #else @@ -3270,6 +3280,12 @@ JANET_CORE_FN(cfun_ev_deadline, tto->vm = &janet_vm; tto->fiber = tocheck; #ifdef JANET_WINDOWS + HANDLE cancel_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (NULL == cancel_event) { + janet_free(tto); + janet_panic("failed to create cancel event"); + } + tto->cancel_event = cancel_event; HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, 0, NULL); if (NULL == worker) { janet_free(tto); @@ -3285,6 +3301,9 @@ JANET_CORE_FN(cfun_ev_deadline, #endif to.has_worker = 1; to.worker = worker; +#ifdef JANET_WINDOWS + to.worker_event = cancel_event; +#endif } else { to.has_worker = 0; } diff --git a/src/core/state.h b/src/core/state.h index 918c1ade..6c9add74 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -65,6 +65,7 @@ typedef struct { int has_worker; #ifdef JANET_WINDOWS HANDLE worker; + HANDLE worker_event; #else pthread_t worker; #endif From b4eb52ca45eeeeb646ed0d2c3bca519d06c0555f Mon Sep 17 00:00:00 2001 From: Michael Camilleri Date: Tue, 16 Sep 2025 11:57:40 +0900 Subject: [PATCH 2/4] Start worker thread in suspended state on Windows --- src/core/ev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/ev.c b/src/core/ev.c index 8422168a..d5aca280 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -3286,7 +3286,7 @@ JANET_CORE_FN(cfun_ev_deadline, janet_panic("failed to create cancel event"); } tto->cancel_event = cancel_event; - HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, 0, NULL); + HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, CREATE_SUSPENDED, NULL); if (NULL == worker) { janet_free(tto); janet_panic("failed to create thread"); @@ -3303,6 +3303,7 @@ JANET_CORE_FN(cfun_ev_deadline, to.worker = worker; #ifdef JANET_WINDOWS to.worker_event = cancel_event; + ResumeThread(worker); #endif } else { to.has_worker = 0; From fe6c6e15a6779623ccce6d3ce96dc1bd2ebe4f00 Mon Sep 17 00:00:00 2001 From: Michael Camilleri Date: Wed, 17 Sep 2025 15:48:37 +0900 Subject: [PATCH 3/4] Add workaround to timer resolution issue on Windows --- src/core/ev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/ev.c b/src/core/ev.c index d5aca280..b3b7edf9 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -700,7 +700,14 @@ static void janet_timeout_cb(JanetEVGenericMessage msg) { static DWORD WINAPI janet_timeout_body(LPVOID ptr) { JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr; janet_free(ptr); - DWORD res = WaitForSingleObject(tto.cancel_event, (DWORD)(tto.sec * 1000)); + JanetTimestamp wait_begin = ts_now(); + DWORD duration = (DWORD)round(tto.sec * 1000); + DWORD res = WAIT_TIMEOUT; + JanetTimestamp wait_end = ts_now(); + for (size_t i = 1; res == WAIT_TIMEOUT && (wait_end - wait_begin) < duration; i++) { + res = WaitForSingleObject(tto.cancel_event, (duration + i)); + wait_end = ts_now(); + } /* only send interrupt message if result is WAIT_TIMEOUT */ if (res == WAIT_TIMEOUT) { janet_interpreter_interrupt(tto.vm); From 6b06ab5f9cf24df3de2829885d82dcccad388ab9 Mon Sep 17 00:00:00 2001 From: Michael Camilleri Date: Wed, 17 Sep 2025 15:51:53 +0900 Subject: [PATCH 4/4] Remove unused function on Windows --- src/core/ev.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/core/ev.c b/src/core/ev.c index b3b7edf9..5e2b1044 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -607,12 +607,7 @@ void janet_ev_init_common(void) { #endif } -#ifdef JANET_WINDOWS -static VOID CALLBACK janet_timeout_stop(ULONG_PTR ptr) { - UNREFERENCED_PARAMETER(ptr); - ExitThread(0); -} -#elif JANET_ANDROID +#if JANET_ANDROID static void janet_timeout_stop(int sig_num) { if (sig_num == SIGUSR1) { pthread_exit(0);