Skip to content

Commit a74a6fa

Browse files
committed
Support os.Worker on Windows
Add thread create/join utility functions and use those to implement os.Worker on the operating system from Redmond.
1 parent 7891004 commit a74a6fa

File tree

5 files changed

+101
-84
lines changed

5 files changed

+101
-84
lines changed

cutils.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,36 @@ int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) {
12671267
return -1;
12681268
}
12691269

1270+
int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg,
1271+
int flags)
1272+
{
1273+
HANDLE h, cp;
1274+
1275+
*thrd = INVALID_HANDLE_VALUE;
1276+
if (flags & ~JS_THREAD_CREATE_DETACHED)
1277+
return -1;
1278+
cp = GetCurrentProcess();
1279+
h = (HANDLE)_beginthread(start, /*stacksize*/2<<20, arg);
1280+
if (!h)
1281+
return -1;
1282+
// _endthread() automatically closes the handle but we want to wait on
1283+
// it so make a copy. Race-y for very short-lived threads. Can be solved
1284+
// by switching to _beginthreadex(CREATE_SUSPENDED) but means changing
1285+
// |start| from __cdecl to __stdcall.
1286+
if (!(flags & JS_THREAD_CREATE_DETACHED))
1287+
if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS))
1288+
return -1;
1289+
return 0;
1290+
}
1291+
1292+
int js_thread_join(js_thread_t thrd)
1293+
{
1294+
if (WaitForSingleObject(thrd, INFINITE))
1295+
return -1;
1296+
CloseHandle(thrd);
1297+
return 0;
1298+
}
1299+
12701300
#else /* !defined(_WIN32) */
12711301

12721302
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) {
14091439

14101440
#endif
14111441

1442+
int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg,
1443+
int flags)
1444+
{
1445+
union {
1446+
void (*x)(void *);
1447+
void *(*f)(void *);
1448+
} u = {start};
1449+
pthread_attr_t attr;
1450+
int ret;
1451+
1452+
if (flags & ~JS_THREAD_CREATE_DETACHED)
1453+
return -1;
1454+
if (pthread_attr_init(&attr))
1455+
return -1;
1456+
ret = -1;
1457+
if (pthread_attr_setstacksize(&attr, 2<<20))
1458+
goto fail;
1459+
if (flags & JS_THREAD_CREATE_DETACHED)
1460+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
1461+
goto fail;
1462+
if (pthread_create(thrd, &attr, u.f, arg))
1463+
goto fail;
1464+
ret = 0;
1465+
fail:
1466+
pthread_attr_destroy(&attr);
1467+
return ret;
1468+
}
1469+
1470+
int js_thread_join(js_thread_t thrd)
1471+
{
1472+
if (pthread_join(thrd, NULL))
1473+
return -1;
1474+
return 0;
1475+
}
1476+
14121477
#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */
14131478

14141479
#ifdef __GNUC__

cutils.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,13 @@ static inline size_t js__malloc_usable_size(const void *ptr)
570570
typedef INIT_ONCE js_once_t;
571571
typedef CRITICAL_SECTION js_mutex_t;
572572
typedef CONDITION_VARIABLE js_cond_t;
573+
typedef HANDLE js_thread_t;
573574
#else
574575
#define JS_ONCE_INIT PTHREAD_ONCE_INIT
575576
typedef pthread_once_t js_once_t;
576577
typedef pthread_mutex_t js_mutex_t;
577578
typedef pthread_cond_t js_cond_t;
579+
typedef pthread_t js_thread_t;
578580
#endif
579581

580582
void js_once(js_once_t *guard, void (*callback)(void));
@@ -591,6 +593,15 @@ void js_cond_broadcast(js_cond_t *cond);
591593
void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex);
592594
int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout);
593595

596+
enum {
597+
JS_THREAD_CREATE_DETACHED = 1,
598+
};
599+
600+
// creates threads with 2 MB stacks (glibc default)
601+
int js_thread_create(js_thread_t *thrd, void (*start)(void *), void *arg,
602+
int flags);
603+
int js_thread_join(js_thread_t thrd);
604+
594605
#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */
595606

596607
#ifdef __cplusplus

quickjs-libc.c

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,12 @@ extern char **environ;
8080

8181
#endif /* _WIN32 */
8282

83-
#if !defined(_WIN32) && !defined(__wasi__)
83+
#if !defined(__wasi__)
8484
/* enable the os.Worker API. IT relies on POSIX threads */
8585
#define USE_WORKER
8686
#endif
8787

8888
#ifdef USE_WORKER
89-
#include <pthread.h>
9089
#include "quickjs-c-atomics.h"
9190
#endif
9291

@@ -143,7 +142,7 @@ typedef struct {
143142
typedef struct {
144143
int ref_count;
145144
#ifdef USE_WORKER
146-
pthread_mutex_t mutex;
145+
js_mutex_t mutex;
147146
#endif
148147
struct list_head msg_queue; /* list of JSWorkerMessage.link */
149148
int read_fd;
@@ -2461,7 +2460,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
24612460
JSWorkerMessage *msg;
24622461
JSValue obj, data_obj, func, retval;
24632462

2464-
pthread_mutex_lock(&ps->mutex);
2463+
js_mutex_lock(&ps->mutex);
24652464
if (!list_empty(&ps->msg_queue)) {
24662465
el = ps->msg_queue.next;
24672466
msg = list_entry(el, JSWorkerMessage, link);
@@ -2481,7 +2480,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
24812480
}
24822481
}
24832482

2484-
pthread_mutex_unlock(&ps->mutex);
2483+
js_mutex_unlock(&ps->mutex);
24852484

24862485
data_obj = JS_ReadObject(ctx, msg->data, msg->data_len,
24872486
JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE);
@@ -2511,7 +2510,7 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
25112510
}
25122511
ret = 1;
25132512
} else {
2514-
pthread_mutex_unlock(&ps->mutex);
2513+
js_mutex_unlock(&ps->mutex);
25152514
ret = 0;
25162515
}
25172516
return ret;
@@ -3499,7 +3498,7 @@ static JSWorkerMessagePipe *js_new_message_pipe(void)
34993498
}
35003499
ps->ref_count = 1;
35013500
init_list_head(&ps->msg_queue);
3502-
pthread_mutex_init(&ps->mutex, NULL);
3501+
js_mutex_init(&ps->mutex);
35033502
ps->read_fd = pipe_fds[0];
35043503
ps->write_fd = pipe_fds[1];
35053504
return ps;
@@ -3539,7 +3538,7 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps)
35393538
msg = list_entry(el, JSWorkerMessage, link);
35403539
js_free_message(msg);
35413540
}
3542-
pthread_mutex_destroy(&ps->mutex);
3541+
js_mutex_destroy(&ps->mutex);
35433542
close(ps->read_fd);
35443543
close(ps->write_fd);
35453544
free(ps);
@@ -3573,7 +3572,7 @@ static JSClassDef js_worker_class = {
35733572
.finalizer = js_worker_finalizer,
35743573
};
35753574

3576-
static void *worker_func(void *opaque)
3575+
static void worker_func(void *opaque)
35773576
{
35783577
WorkerFuncArgs *args = opaque;
35793578
JSRuntime *rt;
@@ -3620,7 +3619,6 @@ static void *worker_func(void *opaque)
36203619
js_std_free_handlers(rt);
36213620
JS_FreeContext(ctx);
36223621
JS_FreeRuntime(rt);
3623-
return NULL;
36243622
}
36253623

36263624
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,
36623660
{
36633661
JSRuntime *rt = JS_GetRuntime(ctx);
36643662
WorkerFuncArgs *args = NULL;
3665-
pthread_t tid;
3666-
pthread_attr_t attr;
3663+
js_thread_t thr;
36673664
JSValue obj = JS_UNDEFINED;
36683665
int ret;
36693666
const char *filename = NULL, *basename;
@@ -3710,14 +3707,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
37103707
if (JS_IsException(obj))
37113708
goto fail;
37123709

3713-
pthread_attr_init(&attr);
3714-
/* no join at the end */
3715-
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
3716-
// musl libc gives threads 80 kb stacks, much smaller than
3717-
// JS_DEFAULT_STACK_SIZE (1 MB)
3718-
pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
3719-
ret = pthread_create(&tid, &attr, worker_func, args);
3720-
pthread_attr_destroy(&attr);
3710+
ret = js_thread_create(&thr, worker_func, args, JS_THREAD_CREATE_DETACHED);
37213711
if (ret != 0) {
37223712
JS_ThrowTypeError(ctx, "could not create worker");
37233713
goto fail;
@@ -3792,7 +3782,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
37923782
}
37933783

37943784
ps = worker->send_pipe;
3795-
pthread_mutex_lock(&ps->mutex);
3785+
js_mutex_lock(&ps->mutex);
37963786
/* indicate that data is present */
37973787
if (list_empty(&ps->msg_queue)) {
37983788
uint8_t ch = '\0';
@@ -3806,7 +3796,7 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
38063796
}
38073797
}
38083798
list_add_tail(&msg->link, &ps->msg_queue);
3809-
pthread_mutex_unlock(&ps->mutex);
3799+
js_mutex_unlock(&ps->mutex);
38103800
return JS_UNDEFINED;
38113801
fail:
38123802
if (msg) {

run-test262.c

Lines changed: 12 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@
3535
#ifdef _WIN32
3636
#include <windows.h>
3737
#include <process.h>
38-
typedef HANDLE js_thread_t;
3938
#else
4039
#include <dirent.h>
41-
#include <pthread.h>
4240
#include <unistd.h>
43-
typedef pthread_t js_thread_t;
4441
#endif
4542

4643
#include "cutils.h"
@@ -530,49 +527,6 @@ static JSValue js_evalScript_262(JSContext *ctx, JSValueConst this_val,
530527
return ret;
531528
}
532529

533-
static void start_thread(js_thread_t *thrd, void *(*start)(void *), void *arg)
534-
{
535-
// musl libc gives threads 80 kb stacks, much smaller than
536-
// JS_DEFAULT_STACK_SIZE (1 MB)
537-
static const unsigned stacksize = 2 << 20; // 2 MB, glibc default
538-
#ifdef _WIN32
539-
HANDLE h, cp;
540-
541-
cp = GetCurrentProcess();
542-
h = (HANDLE)_beginthread((void (*)(void *))(void *)start, stacksize, arg);
543-
if (!h)
544-
fatal(1, "_beginthread error");
545-
// _endthread() automatically closes the handle but we want to wait on
546-
// it so make a copy. Race-y for very short-lived threads. Can be solved
547-
// by switching to _beginthreadex(CREATE_SUSPENDED) but means changing
548-
// |start| from __cdecl to __stdcall.
549-
if (!DuplicateHandle(cp, h, cp, thrd, 0, false, DUPLICATE_SAME_ACCESS))
550-
fatal(1, "DuplicateHandle error");
551-
#else
552-
pthread_attr_t attr;
553-
554-
if (pthread_attr_init(&attr))
555-
fatal(1, "pthread_attr_init");
556-
if (pthread_attr_setstacksize(&attr, stacksize))
557-
fatal(1, "pthread_attr_setstacksize");
558-
if (pthread_create(thrd, &attr, start, arg))
559-
fatal(1, "pthread_create error");
560-
pthread_attr_destroy(&attr);
561-
#endif
562-
}
563-
564-
static void join_thread(js_thread_t thrd)
565-
{
566-
#ifdef _WIN32
567-
if (WaitForSingleObject(thrd, INFINITE))
568-
fatal(1, "WaitForSingleObject error");
569-
CloseHandle(thrd);
570-
#else
571-
if (pthread_join(thrd, NULL))
572-
fatal(1, "pthread_join error");
573-
#endif
574-
}
575-
576530
static long cpu_count(void)
577531
{
578532
#ifdef _WIN32
@@ -621,7 +575,7 @@ typedef struct {
621575
static JSValue add_helpers1(JSContext *ctx);
622576
static void add_helpers(JSContext *ctx);
623577

624-
static void *agent_start(void *arg)
578+
static void agent_start(void *arg)
625579
{
626580
ThreadLocalStorage *tls;
627581
Test262Agent *agent;
@@ -697,7 +651,6 @@ static void *agent_start(void *arg)
697651

698652
JS_FreeContext(ctx);
699653
JS_FreeRuntime(rt);
700-
return NULL;
701654
}
702655

703656
static JSValue js_agent_start(JSContext *ctx, JSValueConst this_val,
@@ -721,7 +674,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValueConst this_val,
721674
agent->tls = tls;
722675
JS_FreeCString(ctx, script);
723676
list_add_tail(&agent->link, &tls->agent_list);
724-
start_thread(&agent->tid, agent_start, agent);
677+
js_thread_create(&agent->tid, agent_start, agent, /*flags*/0);
725678
return JS_UNDEFINED;
726679
}
727680

@@ -733,7 +686,7 @@ static void js_agent_free(JSContext *ctx)
733686

734687
list_for_each_safe(el, el1, &tls->agent_list) {
735688
agent = list_entry(el, Test262Agent, link);
736-
join_thread(agent->tid);
689+
js_thread_join(agent->tid);
737690
JS_FreeValue(ctx, agent->broadcast_sab);
738691
list_del(&agent->link);
739692
free(agent);
@@ -2083,7 +2036,7 @@ int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename,
20832036

20842037
clock_t last_clock;
20852038

2086-
void *show_progress(void *unused) {
2039+
void show_progress(void *unused) {
20872040
int interval = 1000*1000*1000 / 4; // 250 ms
20882041

20892042
js_mutex_lock(&progress_mutex);
@@ -2096,7 +2049,6 @@ void *show_progress(void *unused) {
20962049
fflush(stderr);
20972050
}
20982051
js_mutex_unlock(&progress_mutex);
2099-
return NULL;
21002052
}
21012053

21022054
enum { INCLUDE, EXCLUDE, SKIP };
@@ -2112,7 +2064,7 @@ int include_exclude_or_skip(int i) // naming is hard...
21122064
return INCLUDE;
21132065
}
21142066

2115-
void *run_test_dir_list(void *arg)
2067+
void run_test_dir_list(void *arg)
21162068
{
21172069
ThreadLocalStorage tls_s, *tls = &tls_s;
21182070
const char *p;
@@ -2129,7 +2081,6 @@ void *run_test_dir_list(void *arg)
21292081
if (verbose > 1 || (slow_test_threshold && msec >= slow_test_threshold))
21302082
fprintf(stderr, "%s (%d ms)\n", p, msec);
21312083
}
2132-
return NULL;
21332084
}
21342085

21352086
void help(void)
@@ -2304,15 +2255,17 @@ int main(int argc, char **argv)
23042255
}
23052256
js_cond_init(&progress_cond);
23062257
js_mutex_init(&progress_mutex);
2307-
start_thread(&progress_thread, show_progress, NULL);
2308-
for (i = 0; i < nthreads; i++)
2309-
start_thread(&threads[i], run_test_dir_list, (void *)(uintptr_t)i);
2258+
js_thread_create(&progress_thread, show_progress, NULL, /*flags*/0);
2259+
for (i = 0; i < nthreads; i++) {
2260+
js_thread_create(&threads[i], run_test_dir_list,
2261+
(void *)(uintptr_t)i, /*flags*/0);
2262+
}
23102263
for (i = 0; i < nthreads; i++)
2311-
join_thread(threads[i]);
2264+
js_thread_join(threads[i]);
23122265
js_mutex_lock(&progress_mutex);
23132266
js_cond_signal(&progress_cond);
23142267
js_mutex_unlock(&progress_mutex);
2315-
join_thread(progress_thread);
2268+
js_thread_join(progress_thread);
23162269
js_mutex_destroy(&progress_mutex);
23172270
js_cond_destroy(&progress_cond);
23182271
} else {

0 commit comments

Comments
 (0)