@scriptc/runtime 0.0.24 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_async.c +235 -6
- package/src/scr_child.c +2 -2
- package/src/scr_events.c +48 -2
- package/src/scr_events_emitter.c +4 -4
- package/src/scr_http.c +1 -0
- package/src/scr_island.c +8 -4
- package/src/scr_lib.c +61 -12
- package/src/scr_musl.c +274 -0
- package/src/scr_net.c +130 -8
- package/src/scr_runtime.h +60 -21
- package/src/scr_tls.c +22 -0
- package/vendor/README.md +8 -0
package/package.json
CHANGED
package/src/scr_async.c
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
* served by a capped nanosleep — dispatch at the next turn's top, the
|
|
38
38
|
* cap bounding signal/stdin latency instead of a pollable wake fd. */
|
|
39
39
|
#include <windows.h>
|
|
40
|
+
#elif defined(__wasi__)
|
|
41
|
+
#include <poll.h>
|
|
40
42
|
#else
|
|
41
43
|
#include <poll.h>
|
|
42
44
|
#include <pthread.h>
|
|
@@ -45,6 +47,23 @@
|
|
|
45
47
|
#include <unistd.h>
|
|
46
48
|
#endif
|
|
47
49
|
|
|
50
|
+
#ifdef __wasi__
|
|
51
|
+
/* WASI Preview 1 has no process-spawn capability. The child unit is not
|
|
52
|
+
* linked for this target, but the generic loop keeps its quiescence hooks;
|
|
53
|
+
* their empty implementation makes the absence explicit at link time
|
|
54
|
+
* without weakening async/timer support. */
|
|
55
|
+
bool scr_children_pending(void) { return false; }
|
|
56
|
+
bool scr_children_reffed_pending(void) { return false; }
|
|
57
|
+
bool scr_children_failed_pending(void) { return false; }
|
|
58
|
+
void scr_children_poll(void) {}
|
|
59
|
+
int scr_children_wake_fd(void) { return -1; }
|
|
60
|
+
bool scr_children_wait(double max_wait_ms) {
|
|
61
|
+
(void)max_wait_ms;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
void scr_children_teardown(void) {}
|
|
65
|
+
#endif
|
|
66
|
+
|
|
48
67
|
#if defined(__has_feature)
|
|
49
68
|
#if __has_feature(address_sanitizer)
|
|
50
69
|
#define SCR_ASAN_FIBERS 1
|
|
@@ -274,12 +293,18 @@ void scr_promise_trace_v(void *p, ScrTraceVisit visit, void *ctx) {
|
|
|
274
293
|
* so the slot only needs to name the destination; `from` is unused. */
|
|
275
294
|
#ifdef _WIN32
|
|
276
295
|
typedef void *ScrCtx;
|
|
296
|
+
#elif defined(__wasi__)
|
|
297
|
+
typedef unsigned char ScrCtx;
|
|
277
298
|
#else
|
|
278
299
|
typedef ucontext_t ScrCtx;
|
|
279
300
|
#endif
|
|
280
301
|
|
|
281
302
|
struct ScrFiber {
|
|
282
303
|
ScrCtx ctx;
|
|
304
|
+
/* wasm32 uses LLVM's stackless switched-coroutine frame instead of a
|
|
305
|
+
* native saved stack. The compiler emits these two generic wrappers in
|
|
306
|
+
* every WASI module. */
|
|
307
|
+
void *coro;
|
|
283
308
|
ScrCtx *return_to; /* whoever resumed us last (spawner or the loop) */
|
|
284
309
|
char *stack; /* POSIX only; Windows fibers own their stack (NULL) */
|
|
285
310
|
ScrPromise *promise; /* the promise this fiber settles (owned +1); NULL
|
|
@@ -350,6 +375,19 @@ long scr_abandoned_fiber_count(void) { return scr_fibers_abandoned; }
|
|
|
350
375
|
bool scr_on_fiber(void) { return scr_current != NULL; }
|
|
351
376
|
void *scr_fiber_self(void) { return scr_current; }
|
|
352
377
|
|
|
378
|
+
#ifdef __wasi__
|
|
379
|
+
extern void scr_wasi_coro_resume(void *handle);
|
|
380
|
+
extern void scr_wasi_coro_destroy(void *handle);
|
|
381
|
+
|
|
382
|
+
void scr_wasi_coro_started(void *handle) {
|
|
383
|
+
if (scr_current == NULL) {
|
|
384
|
+
fputs("scriptc: internal error: coroutine started outside a fiber\n", stderr);
|
|
385
|
+
abort();
|
|
386
|
+
}
|
|
387
|
+
scr_current->coro = handle;
|
|
388
|
+
}
|
|
389
|
+
#endif
|
|
390
|
+
|
|
353
391
|
/* Microtask queue: ready fibers, FIFO. */
|
|
354
392
|
static ScrFiber **scr_ready = NULL;
|
|
355
393
|
static size_t scr_ready_head = 0, scr_ready_len = 0, scr_ready_cap = 0;
|
|
@@ -867,6 +905,10 @@ static void scr_switch(ScrCtx *from, ScrCtx *to, ScrFiber *to_fiber) {
|
|
|
867
905
|
* fiber object — `from` has nothing to record. */
|
|
868
906
|
(void)from;
|
|
869
907
|
SwitchToFiber(*to);
|
|
908
|
+
#elif defined(__wasi__)
|
|
909
|
+
(void)from;
|
|
910
|
+
(void)to;
|
|
911
|
+
scr_trap("scriptc: internal error: native stack switch reached on WASI\n");
|
|
870
912
|
#elif defined(SCR_ASAN_FIBERS)
|
|
871
913
|
void **save = from_fiber ? &from_fiber->fake_stack : &scr_main_fake_stack;
|
|
872
914
|
const void *bottom = to_fiber ? to_fiber->stack : NULL;
|
|
@@ -1154,6 +1196,8 @@ static void scr_trampoline(void) {
|
|
|
1154
1196
|
static void scr_fiber_destroy(ScrFiber *f) {
|
|
1155
1197
|
#ifdef _WIN32
|
|
1156
1198
|
DeleteFiber(f->ctx);
|
|
1199
|
+
#elif defined(__wasi__)
|
|
1200
|
+
if (f->coro != NULL) scr_wasi_coro_destroy(f->coro);
|
|
1157
1201
|
#endif
|
|
1158
1202
|
scr_als_ctx_release(f->als);
|
|
1159
1203
|
free(f->stack);
|
|
@@ -1179,6 +1223,22 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1179
1223
|
ScrCtx here = scr_win_self();
|
|
1180
1224
|
f->ctx = CreateFiber(SCR_FIBER_STACK, scr_trampoline, NULL);
|
|
1181
1225
|
if (f->ctx == NULL) scr_oom();
|
|
1226
|
+
#elif defined(__wasi__)
|
|
1227
|
+
ScrFiber *spawner = scr_current;
|
|
1228
|
+
scr_current = f;
|
|
1229
|
+
scr_exc_swap_cell(&f->exc);
|
|
1230
|
+
scr_als_active = &f->als;
|
|
1231
|
+
entry(f, argpack); /* eager prefix; returns at suspend/final suspend */
|
|
1232
|
+
scr_current = spawner;
|
|
1233
|
+
scr_exc_swap_cell(spawner ? &spawner->exc : NULL);
|
|
1234
|
+
scr_als_active = spawner ? &spawner->als : &scr_als_main_slot;
|
|
1235
|
+
ScrPromise *result = scr_promise_retain(f->promise);
|
|
1236
|
+
if (f->done) {
|
|
1237
|
+
scr_promise_release(f->promise);
|
|
1238
|
+
scr_fiber_destroy(f);
|
|
1239
|
+
scr_fibers_live--;
|
|
1240
|
+
}
|
|
1241
|
+
return result;
|
|
1182
1242
|
#else
|
|
1183
1243
|
f->stack = malloc(SCR_FIBER_STACK);
|
|
1184
1244
|
if (!f->stack) scr_oom();
|
|
@@ -1190,6 +1250,7 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1190
1250
|
|
|
1191
1251
|
ucontext_t here;
|
|
1192
1252
|
#endif
|
|
1253
|
+
#ifndef __wasi__
|
|
1193
1254
|
f->return_to = &here;
|
|
1194
1255
|
ScrFiber *spawner = scr_current;
|
|
1195
1256
|
scr_switch(&here, &f->ctx, f);
|
|
@@ -1209,6 +1270,42 @@ ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack) {
|
|
|
1209
1270
|
scr_fibers_live--;
|
|
1210
1271
|
}
|
|
1211
1272
|
return result;
|
|
1273
|
+
#endif
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
/* A runtime-authored C continuation whose first operation awaits one known
|
|
1277
|
+
* dependency. Native fibers can park normally. wasm32 has no resumable C
|
|
1278
|
+
* stack, so queue the fiber only after the dependency settles; the entry's
|
|
1279
|
+
* await then extracts the result without attempting a stack switch. The
|
|
1280
|
+
* READY queue supplies the settled-await microtask hop in both cases. */
|
|
1281
|
+
ScrPromise *scr_async_spawn_after(ScrPromise *dependency,
|
|
1282
|
+
void (*entry)(ScrFiber *, void *),
|
|
1283
|
+
void *argpack) {
|
|
1284
|
+
#ifndef __wasi__
|
|
1285
|
+
(void)dependency;
|
|
1286
|
+
return scr_async_spawn(entry, argpack);
|
|
1287
|
+
#else
|
|
1288
|
+
ScrFiber *f = calloc(1, sizeof *f);
|
|
1289
|
+
if (!f) scr_oom();
|
|
1290
|
+
f->promise = scr_promise_new();
|
|
1291
|
+
f->entry = entry;
|
|
1292
|
+
f->argpack = argpack;
|
|
1293
|
+
f->als = scr_als_ctx_retain(*scr_als_active);
|
|
1294
|
+
scr_fibers_live++;
|
|
1295
|
+
|
|
1296
|
+
if (dependency->state == SCR_PROM_PENDING) {
|
|
1297
|
+
if (dependency->nwaiters == dependency->waiters_cap) {
|
|
1298
|
+
dependency->waiters_cap = dependency->waiters_cap ? dependency->waiters_cap * 2 : 4;
|
|
1299
|
+
dependency->waiters = realloc(
|
|
1300
|
+
dependency->waiters, dependency->waiters_cap * sizeof *dependency->waiters);
|
|
1301
|
+
if (!dependency->waiters) scr_oom();
|
|
1302
|
+
}
|
|
1303
|
+
dependency->waiters[dependency->nwaiters++] = f;
|
|
1304
|
+
} else {
|
|
1305
|
+
scr_ready_push(f);
|
|
1306
|
+
}
|
|
1307
|
+
return scr_promise_retain(f->promise);
|
|
1308
|
+
#endif
|
|
1212
1309
|
}
|
|
1213
1310
|
|
|
1214
1311
|
/* Parks the current fiber on `p` until it settles. Only fibers await. */
|
|
@@ -1228,6 +1325,42 @@ static void scr_await_park(ScrPromise *p) {
|
|
|
1228
1325
|
/* Resumed by the loop: return_to must now point at the loop's context. */
|
|
1229
1326
|
}
|
|
1230
1327
|
|
|
1328
|
+
#ifdef __wasi__
|
|
1329
|
+
/* The emitted coroutine suspends immediately after these preparation
|
|
1330
|
+
* calls. Settled promises still queue one ready turn; pending promises own
|
|
1331
|
+
* the continuation through their waiter list. */
|
|
1332
|
+
void scr_wasi_await_prepare(ScrFiber *self, ScrPromise *p) {
|
|
1333
|
+
if (p->state == SCR_PROM_PENDING) {
|
|
1334
|
+
if (p->nwaiters == p->waiters_cap) {
|
|
1335
|
+
p->waiters_cap = p->waiters_cap ? p->waiters_cap * 2 : 4;
|
|
1336
|
+
p->waiters = realloc(p->waiters, p->waiters_cap * sizeof *p->waiters);
|
|
1337
|
+
if (!p->waiters) scr_oom();
|
|
1338
|
+
}
|
|
1339
|
+
p->waiters[p->nwaiters++] = self;
|
|
1340
|
+
} else {
|
|
1341
|
+
scr_ready_push(self);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
void scr_wasi_await_hop_prepare(ScrFiber *self) { scr_ready_push(self); }
|
|
1346
|
+
|
|
1347
|
+
bool scr_wasi_module_await_prepare(ScrFiber *self, ScrPromise *p) {
|
|
1348
|
+
if (p->state != SCR_PROM_PENDING) return false;
|
|
1349
|
+
scr_wasi_await_prepare(self, p);
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
void scr_wasi_async_finish(ScrFiber *self) { scr_fiber_finish(self); }
|
|
1354
|
+
|
|
1355
|
+
void scr_wasi_gen_finish(ScrFiber *self) {
|
|
1356
|
+
if (scr_exc_genret_pending()) {
|
|
1357
|
+
scr_exc_clear();
|
|
1358
|
+
scr_gen_ret_to_out(self->gen);
|
|
1359
|
+
}
|
|
1360
|
+
scr_fiber_finish(self);
|
|
1361
|
+
}
|
|
1362
|
+
#endif
|
|
1363
|
+
|
|
1231
1364
|
/* One microtask hop: park the current fiber on the READY queue itself and
|
|
1232
1365
|
* yield. JS's `await` ALWAYS suspends — even on an already-settled promise
|
|
1233
1366
|
* the continuation runs as a microtask — so without this hop an awaiter of
|
|
@@ -1268,8 +1401,15 @@ static void scr_promise_rethrow(ScrPromise *p) {
|
|
|
1268
1401
|
|
|
1269
1402
|
/* Await result extraction. Rejection re-throws into the awaiter. */
|
|
1270
1403
|
static bool scr_await_settled(ScrPromise *p) {
|
|
1404
|
+
#ifdef __wasi__
|
|
1405
|
+
if (p->state == SCR_PROM_PENDING) {
|
|
1406
|
+
fputs("scriptc: internal error: resumed before awaited promise settled\n", stderr);
|
|
1407
|
+
abort();
|
|
1408
|
+
}
|
|
1409
|
+
#else
|
|
1271
1410
|
if (p->state != SCR_PROM_PENDING) scr_await_yield();
|
|
1272
1411
|
while (p->state == SCR_PROM_PENDING) scr_await_park(p);
|
|
1412
|
+
#endif
|
|
1273
1413
|
scr_prom_observe(p);
|
|
1274
1414
|
if (p->state == SCR_PROM_REJECTED) {
|
|
1275
1415
|
scr_promise_rethrow(p);
|
|
@@ -1295,7 +1435,9 @@ void scr_await_void(ScrPromise *p) { scr_await_settled(p); }
|
|
|
1295
1435
|
* dependency still parks this module fiber, and rejection propagates into
|
|
1296
1436
|
* it exactly like an ordinary await. */
|
|
1297
1437
|
void scr_module_await(ScrPromise *p) {
|
|
1438
|
+
#ifndef __wasi__
|
|
1298
1439
|
while (p->state == SCR_PROM_PENDING) scr_await_park(p);
|
|
1440
|
+
#endif
|
|
1299
1441
|
scr_prom_observe(p);
|
|
1300
1442
|
if (p->state == SCR_PROM_REJECTED) scr_promise_rethrow(p);
|
|
1301
1443
|
}
|
|
@@ -1524,7 +1666,7 @@ ScrPromise *scr_fsp_rename(ScrStr *oldpath, ScrStr *newpath) {
|
|
|
1524
1666
|
* libuv's default filesystem concurrency without creating one OS thread per
|
|
1525
1667
|
* request. A platform mutex publishes each result and the syscall's filesystem
|
|
1526
1668
|
* effects before the loop removes it from the completion queue. */
|
|
1527
|
-
#
|
|
1669
|
+
#if !defined(SCR_LIB) && !defined(__wasi__)
|
|
1528
1670
|
typedef struct ScrFsRenameOp {
|
|
1529
1671
|
ScrStr *oldpath;
|
|
1530
1672
|
ScrStr *newpath;
|
|
@@ -1678,6 +1820,49 @@ static bool scr_fs_renames_dispatch(void) {
|
|
|
1678
1820
|
scr_fs_rename_op_release(op);
|
|
1679
1821
|
return true;
|
|
1680
1822
|
}
|
|
1823
|
+
#elif defined(__wasi__)
|
|
1824
|
+
/* WASI Preview 1 has no threads, but rename itself is available. Queue the
|
|
1825
|
+
* request and perform it on the next loop turn so callback timing remains
|
|
1826
|
+
* asynchronous and the request remains a liveness handle. */
|
|
1827
|
+
typedef struct ScrFsRenameOp {
|
|
1828
|
+
ScrStr *oldpath;
|
|
1829
|
+
ScrStr *newpath;
|
|
1830
|
+
ScrClosure *cb;
|
|
1831
|
+
ScrFsRenameFn fn;
|
|
1832
|
+
struct ScrFsRenameOp *next;
|
|
1833
|
+
} ScrFsRenameOp;
|
|
1834
|
+
|
|
1835
|
+
static ScrFsRenameOp *scr_fs_rename_done = NULL;
|
|
1836
|
+
static ScrFsRenameOp **scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1837
|
+
static size_t scr_fs_rename_pending_count = 0;
|
|
1838
|
+
|
|
1839
|
+
static bool scr_fs_renames_pending(void) { return scr_fs_rename_pending_count != 0; }
|
|
1840
|
+
|
|
1841
|
+
static bool scr_fs_renames_dispatch(void) {
|
|
1842
|
+
ScrFsRenameOp *op = scr_fs_rename_done;
|
|
1843
|
+
if (op == NULL) return false;
|
|
1844
|
+
scr_fs_rename_done = op->next;
|
|
1845
|
+
if (scr_fs_rename_done == NULL) scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1846
|
+
scr_fs_rename_pending_count--;
|
|
1847
|
+
|
|
1848
|
+
int error = scr_fs_rename_raw(op->oldpath, op->newpath);
|
|
1849
|
+
ScrCaught *caught = NULL;
|
|
1850
|
+
ScrError *err = NULL;
|
|
1851
|
+
if (error != 0) {
|
|
1852
|
+
scr_fs_rename_error(error, op->oldpath, op->newpath);
|
|
1853
|
+
caught = scr_exc_take();
|
|
1854
|
+
if (caught && caught->kind == SCR_EXC_OBJ && scr_error_is(caught->payload)) {
|
|
1855
|
+
err = (ScrError *)caught->payload;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
op->fn(op->cb, err);
|
|
1859
|
+
scr_caught_release(caught);
|
|
1860
|
+
scr_str_release(op->oldpath);
|
|
1861
|
+
scr_str_release(op->newpath);
|
|
1862
|
+
scr_closure_release(op->cb);
|
|
1863
|
+
free(op);
|
|
1864
|
+
return true;
|
|
1865
|
+
}
|
|
1681
1866
|
#else
|
|
1682
1867
|
static bool scr_fs_renames_pending(void) { return false; }
|
|
1683
1868
|
static bool scr_fs_renames_dispatch(void) { return false; }
|
|
@@ -1685,10 +1870,20 @@ static bool scr_fs_renames_dispatch(void) { return false; }
|
|
|
1685
1870
|
|
|
1686
1871
|
void scr_fs_rename_async(ScrStr *oldpath, ScrStr *newpath,
|
|
1687
1872
|
ScrClosure *cb, ScrFsRenameFn fn) {
|
|
1688
|
-
#
|
|
1873
|
+
#if defined(SCR_LIB)
|
|
1689
1874
|
(void)oldpath; (void)newpath; (void)fn;
|
|
1690
1875
|
scr_closure_release(cb);
|
|
1691
|
-
scr_trap("scriptc:
|
|
1876
|
+
scr_trap("scriptc: callback-style fs.rename is not supported by this target\n");
|
|
1877
|
+
#elif defined(__wasi__)
|
|
1878
|
+
ScrFsRenameOp *op = calloc(1, sizeof *op);
|
|
1879
|
+
if (!op) scr_trap("scriptc: out of memory\n");
|
|
1880
|
+
op->oldpath = scr_str_retain(oldpath);
|
|
1881
|
+
op->newpath = scr_str_retain(newpath);
|
|
1882
|
+
op->cb = cb;
|
|
1883
|
+
op->fn = fn;
|
|
1884
|
+
*scr_fs_rename_done_tail = op;
|
|
1885
|
+
scr_fs_rename_done_tail = &op->next;
|
|
1886
|
+
scr_fs_rename_pending_count++;
|
|
1692
1887
|
#else
|
|
1693
1888
|
if (!scr_fs_rename_shutdown_registered) {
|
|
1694
1889
|
if (atexit(scr_fs_renames_shutdown) != 0) {
|
|
@@ -1893,7 +2088,25 @@ static void scr_resume_fiber(ScrFiber *f) {
|
|
|
1893
2088
|
(void)scr_win_self();
|
|
1894
2089
|
#endif
|
|
1895
2090
|
f->return_to = &scr_loop_ctx;
|
|
2091
|
+
#ifdef __wasi__
|
|
2092
|
+
scr_current = f;
|
|
2093
|
+
scr_exc_swap_cell(&f->exc);
|
|
2094
|
+
scr_als_active = &f->als;
|
|
2095
|
+
if (f->coro != NULL) {
|
|
2096
|
+
scr_wasi_coro_resume(f->coro);
|
|
2097
|
+
} else {
|
|
2098
|
+
/* scr_async_spawn_after's runtime C continuation. Its dependency is
|
|
2099
|
+
* settled, so the entry can extract through scr_await_* without a
|
|
2100
|
+
* stack switch and then completes in this turn. */
|
|
2101
|
+
f->entry(f, f->argpack);
|
|
2102
|
+
scr_fiber_finish(f);
|
|
2103
|
+
}
|
|
2104
|
+
scr_current = NULL;
|
|
2105
|
+
scr_exc_swap_cell(NULL);
|
|
2106
|
+
scr_als_active = &scr_als_main_slot;
|
|
2107
|
+
#else
|
|
1896
2108
|
scr_switch(&scr_loop_ctx, &f->ctx, f);
|
|
2109
|
+
#endif
|
|
1897
2110
|
if (f->done) {
|
|
1898
2111
|
scr_promise_release(f->promise);
|
|
1899
2112
|
scr_fiber_destroy(f);
|
|
@@ -2222,8 +2435,9 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
2222
2435
|
now = scr_now_ms();
|
|
2223
2436
|
if (scr_ready_len > 0) continue; /* io callbacks woke fibers */
|
|
2224
2437
|
} else if (evw || net || dgram || watch) {
|
|
2225
|
-
#
|
|
2226
|
-
/* The win32 arm
|
|
2438
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
2439
|
+
/* The win32 arm, and WASI hosts whose poll_oneoff adapters do not
|
|
2440
|
+
* reliably wake for a closed inherited stdin pipe: the sleep is a capped nanosleep and
|
|
2227
2441
|
* the next turn's dispatch does the work — signal flags (set by the
|
|
2228
2442
|
* CRT handler on msvcrt's console-ctrl thread) and the stdin probe
|
|
2229
2443
|
* at SCR_SIGNAL_POLL_MS granularity (scr_events.c), socket/dgram
|
|
@@ -2315,7 +2529,7 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
2315
2529
|
* WNOHANG sweep at the next turn's top stays the reaper. */
|
|
2316
2530
|
if (kids) (void)scr_children_wait(0);
|
|
2317
2531
|
now = scr_now_ms();
|
|
2318
|
-
#endif /* !_WIN32 */
|
|
2532
|
+
#endif /* !_WIN32 && !__wasi__ */
|
|
2319
2533
|
} else if (kids && scr_children_wait(due > now ? due - now : 0)) {
|
|
2320
2534
|
now = scr_now_ms(); /* woke early on an exit, or hit the deadline */
|
|
2321
2535
|
} else {
|
|
@@ -2727,6 +2941,8 @@ ScrGen *scr_gen_new(void (*entry)(ScrFiber *, void *), void *argpack,
|
|
|
2727
2941
|
#ifdef _WIN32
|
|
2728
2942
|
f->ctx = CreateFiber(SCR_FIBER_STACK, scr_trampoline, NULL);
|
|
2729
2943
|
if (f->ctx == NULL) scr_oom();
|
|
2944
|
+
#elif defined(__wasi__)
|
|
2945
|
+
f->ctx = 0;
|
|
2730
2946
|
#else
|
|
2731
2947
|
f->stack = malloc(SCR_FIBER_STACK);
|
|
2732
2948
|
if (!f->stack) scr_oom();
|
|
@@ -2898,16 +3114,29 @@ static void scr_gen_switch_in(ScrGen *g) {
|
|
|
2898
3114
|
g->state = SCR_GEN_RUNNING;
|
|
2899
3115
|
#ifdef _WIN32
|
|
2900
3116
|
ScrCtx here = scr_win_self();
|
|
3117
|
+
#elif defined(__wasi__)
|
|
3118
|
+
ScrCtx here = 0;
|
|
2901
3119
|
#else
|
|
2902
3120
|
ucontext_t here;
|
|
2903
3121
|
#endif
|
|
2904
3122
|
f->return_to = &here;
|
|
2905
3123
|
ScrFiber *me = scr_current;
|
|
3124
|
+
#ifdef __wasi__
|
|
3125
|
+
scr_current = f;
|
|
3126
|
+
scr_exc_swap_cell(&f->exc);
|
|
3127
|
+
scr_als_active = &f->als;
|
|
3128
|
+
if (f->coro == NULL) f->entry(f, f->argpack);
|
|
3129
|
+
else scr_wasi_coro_resume(f->coro);
|
|
3130
|
+
scr_current = me;
|
|
3131
|
+
scr_exc_swap_cell(me != NULL ? &me->exc : NULL);
|
|
3132
|
+
scr_als_active = me != NULL ? &me->als : &scr_als_main_slot;
|
|
3133
|
+
#else
|
|
2906
3134
|
scr_switch(&here, &f->ctx, f);
|
|
2907
3135
|
/* Back on the consumer: restore identity + the consumer's cell (the
|
|
2908
3136
|
* yield/finish switch targeted NULL — main's cell). */
|
|
2909
3137
|
scr_current = me;
|
|
2910
3138
|
scr_exc_swap_cell(me != NULL ? &me->exc : NULL);
|
|
3139
|
+
#endif
|
|
2911
3140
|
if (f->done) {
|
|
2912
3141
|
g->state = SCR_GEN_DONE;
|
|
2913
3142
|
if (f->exc.kind != SCR_EXC_NONE) {
|
package/src/scr_child.c
CHANGED
|
@@ -2292,7 +2292,7 @@ ScrStr *scr_exec_sync_core(ScrStr *cmd, ScrArr *args, const ScrExecOpts *o) {
|
|
|
2292
2292
|
} else if (o->stderr_mode != 3) {
|
|
2293
2293
|
posix_spawn_file_actions_addopen(&fa, 2, "/dev/null", O_WRONLY, 0);
|
|
2294
2294
|
} /* mode 3 (inherit): the child keeps the parent's fd 2 */
|
|
2295
|
-
#if defined(__APPLE__) || defined(__GLIBC__)
|
|
2295
|
+
#if defined(__APPLE__) || defined(__GLIBC__) || defined(SCR_MUSL)
|
|
2296
2296
|
if (o->cwd != NULL) {
|
|
2297
2297
|
posix_spawn_file_actions_addchdir_np(&fa, o->cwd->data);
|
|
2298
2298
|
}
|
|
@@ -3180,7 +3180,7 @@ ScrChild *scr_spawn_opts(ScrStr *cmd, ScrArr *args, double in_mode,
|
|
|
3180
3180
|
} else if ((int)err_mode == 3) {
|
|
3181
3181
|
posix_spawn_file_actions_adddup2(&fa, err_pipe[1], 2);
|
|
3182
3182
|
}
|
|
3183
|
-
#if defined(__APPLE__) || defined(__GLIBC__)
|
|
3183
|
+
#if defined(__APPLE__) || defined(__GLIBC__) || defined(SCR_MUSL)
|
|
3184
3184
|
if (cwd != NULL && cwd->len > 0) {
|
|
3185
3185
|
posix_spawn_file_actions_addchdir_np(&fa, cwd->data);
|
|
3186
3186
|
}
|
package/src/scr_events.c
CHANGED
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
#include <io.h>
|
|
31
31
|
#include <unistd.h> /* mingw-w64 ships one: read */
|
|
32
32
|
#include <windows.h>
|
|
33
|
+
#elif defined(__wasi__)
|
|
34
|
+
#include <poll.h>
|
|
35
|
+
#include <unistd.h>
|
|
33
36
|
#else
|
|
34
37
|
#include <poll.h>
|
|
35
38
|
#include <unistd.h>
|
|
@@ -40,6 +43,21 @@ static void scr_events_oom(void) {
|
|
|
40
43
|
abort();
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
#ifdef __wasi__
|
|
47
|
+
/* The native child unit normally owns these generic Error-listener adapters.
|
|
48
|
+
* WASI cannot spawn children and therefore does not link that unit, while
|
|
49
|
+
* stdin's error event intentionally shares the same adapter ABI. */
|
|
50
|
+
void scr_child_err_thunk0(ScrClosure *cb, ScrStr *msg) {
|
|
51
|
+
(void)msg;
|
|
52
|
+
((void (*)(ScrClosure *))cb->fn)(cb);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void scr_child_err_thunk_error(ScrClosure *cb, ScrStr *msg) {
|
|
56
|
+
ScrError *error = scr_error_new(0 /* Error */, msg);
|
|
57
|
+
((void (*)(ScrClosure *, ScrError *))cb->fn)(cb, error);
|
|
58
|
+
}
|
|
59
|
+
#endif
|
|
60
|
+
|
|
43
61
|
/* ── process signal events (process.on("SIGINT" | "SIGTERM")) ─────────
|
|
44
62
|
* Classic self-pipe integration: a watched signal's sigaction handler
|
|
45
63
|
* (installed WITHOUT SA_RESTART) sets a per-signal flag and writes one
|
|
@@ -62,7 +80,7 @@ typedef struct {
|
|
|
62
80
|
|
|
63
81
|
typedef struct ScrSigReg {
|
|
64
82
|
int sig;
|
|
65
|
-
#
|
|
83
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
66
84
|
void (*prev)(int); /* restored when the last listener leaves */
|
|
67
85
|
#else
|
|
68
86
|
struct sigaction prev; /* restored when the last listener leaves */
|
|
@@ -116,7 +134,7 @@ static void scr_sig_cleanup(void) {
|
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
static void scr_wake_pipe_init(void) {
|
|
119
|
-
#
|
|
137
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
120
138
|
/* No self-pipe: the win32 loop never polls fds (scr_async.c's capped
|
|
121
139
|
* nanosleep takes the idle sleep), so the handler's flag is the whole
|
|
122
140
|
* wake path and the fds stay -1 — every pipe use below is guarded. */
|
|
@@ -141,6 +159,15 @@ static void scr_wake_pipe_drain(void) {
|
|
|
141
159
|
}
|
|
142
160
|
|
|
143
161
|
void scr_signal_on(double signum, ScrClosure *cb /*moves*/, bool once) {
|
|
162
|
+
#ifdef __wasi__
|
|
163
|
+
(void)signum;
|
|
164
|
+
(void)once;
|
|
165
|
+
scr_closure_release(cb);
|
|
166
|
+
/* The compiler rejects this capability before linking. This guard keeps
|
|
167
|
+
* the optional events unit honest if hand-authored IR reaches it. */
|
|
168
|
+
scr_trap("scriptc: internal error: OS signal surface reached on WASI\n");
|
|
169
|
+
return;
|
|
170
|
+
#else
|
|
144
171
|
int sig = (int)signum;
|
|
145
172
|
if (sig <= 0 || sig >= SCR_SIG_MAX) {
|
|
146
173
|
scr_closure_release(cb); /* frontend only emits classic signals */
|
|
@@ -176,11 +203,15 @@ void scr_signal_on(double signum, ScrClosure *cb /*moves*/, bool once) {
|
|
|
176
203
|
reg->ls[reg->n].cb = cb;
|
|
177
204
|
reg->ls[reg->n].once = once;
|
|
178
205
|
reg->n++;
|
|
206
|
+
#endif
|
|
179
207
|
}
|
|
180
208
|
|
|
181
209
|
static void scr_sig_reg_drop(ScrSigReg *reg) {
|
|
182
210
|
#ifdef _WIN32
|
|
183
211
|
signal(reg->sig, reg->prev); /* default disposition back */
|
|
212
|
+
#elif defined(__wasi__)
|
|
213
|
+
/* No signal disposition exists in WASI Preview 1. The compiler refuses
|
|
214
|
+
* signal registrations, so this arm is only the cleanup safety net. */
|
|
184
215
|
#else
|
|
185
216
|
sigaction(reg->sig, ®->prev, NULL); /* default disposition back */
|
|
186
217
|
#endif
|
|
@@ -193,6 +224,12 @@ static void scr_sig_reg_drop(ScrSigReg *reg) {
|
|
|
193
224
|
}
|
|
194
225
|
|
|
195
226
|
void scr_signal_off(double signum, ScrClosure *cb /*borrowed*/) {
|
|
227
|
+
#ifdef __wasi__
|
|
228
|
+
(void)signum;
|
|
229
|
+
(void)cb;
|
|
230
|
+
scr_trap("scriptc: internal error: OS signal surface reached on WASI\n");
|
|
231
|
+
return;
|
|
232
|
+
#else
|
|
196
233
|
int sig = (int)signum;
|
|
197
234
|
ScrSigReg *reg = scr_sig_regs;
|
|
198
235
|
while (reg && reg->sig != sig) reg = reg->next;
|
|
@@ -206,6 +243,7 @@ void scr_signal_off(double signum, ScrClosure *cb /*borrowed*/) {
|
|
|
206
243
|
return;
|
|
207
244
|
}
|
|
208
245
|
}
|
|
246
|
+
#endif
|
|
209
247
|
}
|
|
210
248
|
|
|
211
249
|
/* One dispatch pass: every flagged signal fires its listener SNAPSHOT
|
|
@@ -486,6 +524,10 @@ static void scr_stdin_service(void) {
|
|
|
486
524
|
if (WaitForSingleObject(h, 0) != WAIT_OBJECT_0) return;
|
|
487
525
|
}
|
|
488
526
|
}
|
|
527
|
+
#elif defined(__wasi__)
|
|
528
|
+
/* install() puts fd 0 in nonblocking mode, so a direct read is both the
|
|
529
|
+
* readiness probe and the operation. This also observes a closed pipe on
|
|
530
|
+
* WASI hosts whose poll_oneoff adapter does not report POLLHUP. */
|
|
489
531
|
#else
|
|
490
532
|
struct pollfd pfd = {0 /* stdin */, POLLIN, 0};
|
|
491
533
|
int rc = poll(&pfd, 1, 0);
|
|
@@ -705,6 +747,10 @@ void scr_events_install(void) {
|
|
|
705
747
|
static bool installed = false;
|
|
706
748
|
if (installed) return;
|
|
707
749
|
installed = true;
|
|
750
|
+
#ifdef __wasi__
|
|
751
|
+
int stdin_flags = fcntl(0, F_GETFL, 0);
|
|
752
|
+
if (stdin_flags >= 0) (void)fcntl(0, F_SETFL, stdin_flags | O_NONBLOCK);
|
|
753
|
+
#endif
|
|
708
754
|
atexit(scr_events_cleanup_atexit);
|
|
709
755
|
atexit(scr_exit_atexit);
|
|
710
756
|
scr_loop_set_events(&scr_events_pending, &scr_events_watching, &scr_events_dispatch,
|
package/src/scr_events_emitter.c
CHANGED
|
@@ -390,10 +390,10 @@ ScrEmitter *scr_emitter_on_via(ScrEmitter *em, ScrStr *name, ScrClosure *orig /*
|
|
|
390
390
|
|
|
391
391
|
/* ── fixed-arity invoke shims (scr_runtime.h's contract) ──────────────
|
|
392
392
|
* Read k pointer-size slots and call the fixed-signature adapter behind
|
|
393
|
-
* cb->fn. The LLVM lane's emit sites pass
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
393
|
+
* cb->fn. The LLVM lane's emit sites pass scalars by pointer to typed stack
|
|
394
|
+
* slots and references directly; the runtime's own emits are pointer-only,
|
|
395
|
+
* so va_arg(ap, void *) reads every tuple a fixed-registered listener can
|
|
396
|
+
* observe on 32- and 64-bit targets. */
|
|
397
397
|
void scr_ee_inv_fixed0(ScrClosure *cb, va_list ap) {
|
|
398
398
|
(void)ap;
|
|
399
399
|
((void (*)(ScrClosure *))cb->fn)(cb);
|
package/src/scr_http.c
CHANGED
|
@@ -2009,6 +2009,7 @@ void scr_http2_stream_undef_call(ScrStr *member) {
|
|
|
2009
2009
|
ScrNetServer *scr_http_create_server(ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn) {
|
|
2010
2010
|
scr_http_install();
|
|
2011
2011
|
ScrNetServer *s = scr_net_create_server(NULL, NULL);
|
|
2012
|
+
scr_net_server_enable_http_timeout_surface(s);
|
|
2012
2013
|
ScrHttpSrvCtx *ctx = calloc(1, sizeof *ctx);
|
|
2013
2014
|
if (!ctx) scr_http_oom();
|
|
2014
2015
|
ctx->proto = SCR_NET_PROTO_HTTP1;
|
package/src/scr_island.c
CHANGED
|
@@ -118,7 +118,7 @@ static void *isl_realloc_fn(void *opaque, void *ptr, size_t size) {
|
|
|
118
118
|
if (!ptr && p) isl_live_allocs++;
|
|
119
119
|
return p;
|
|
120
120
|
}
|
|
121
|
-
static size_t isl_usable_size(const void *ptr) { return isl_malloc_size(ptr); }
|
|
121
|
+
static size_t isl_usable_size(const void *ptr) { return isl_malloc_size((void *)ptr); }
|
|
122
122
|
|
|
123
123
|
static const JSMallocFunctions isl_mf = {
|
|
124
124
|
isl_calloc, isl_malloc, isl_free, isl_realloc_fn, isl_usable_size,
|
|
@@ -2107,7 +2107,7 @@ ScrJsval *scr_jsval_from_promise(ScrPromise *p, int payload) {
|
|
|
2107
2107
|
w->payload = payload;
|
|
2108
2108
|
w->next = isl_prom_wraps;
|
|
2109
2109
|
isl_prom_wraps = w;
|
|
2110
|
-
ScrPromise *waiter =
|
|
2110
|
+
ScrPromise *waiter = scr_async_spawn_after(w->p, isl_prom_wrap_entry, w);
|
|
2111
2111
|
scr_promise_release(waiter); /* the waiter never rejects; nobody awaits it */
|
|
2112
2112
|
return isl_cell_new(prom);
|
|
2113
2113
|
}
|
|
@@ -3352,7 +3352,7 @@ static JSValue isl_host_ids(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
3352
3352
|
(void)argc;
|
|
3353
3353
|
(void)argv;
|
|
3354
3354
|
JSValue arr = JS_NewArray(ctx);
|
|
3355
|
-
#
|
|
3355
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
3356
3356
|
JS_SetPropertyUint32(ctx, arr, 0, JS_NewInt32(ctx, -1));
|
|
3357
3357
|
JS_SetPropertyUint32(ctx, arr, 1, JS_NewInt32(ctx, -1));
|
|
3358
3358
|
#else
|
|
@@ -3370,7 +3370,7 @@ static JSValue isl_host_umask(JSContext *ctx, JSValueConst this_val, int argc,
|
|
|
3370
3370
|
(void)this_val;
|
|
3371
3371
|
(void)argc;
|
|
3372
3372
|
(void)argv;
|
|
3373
|
-
#
|
|
3373
|
+
#if defined(_WIN32) || defined(__wasi__)
|
|
3374
3374
|
return JS_NewInt32(ctx, 0);
|
|
3375
3375
|
#else
|
|
3376
3376
|
mode_t m = umask(0);
|
|
@@ -3495,11 +3495,15 @@ static JSValue isl_host_hostname(JSContext *ctx, JSValueConst this_val, int argc
|
|
|
3495
3495
|
* counted like the socket units' own WSAStartup calls (ws2_32 is on
|
|
3496
3496
|
* every win32 link line). */
|
|
3497
3497
|
char buf[256];
|
|
3498
|
+
#if defined(__wasi__)
|
|
3499
|
+
buf[0] = '\0';
|
|
3500
|
+
#else
|
|
3498
3501
|
#ifdef _WIN32
|
|
3499
3502
|
WSADATA wsa;
|
|
3500
3503
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return JS_NewString(ctx, "");
|
|
3501
3504
|
#endif
|
|
3502
3505
|
if (gethostname(buf, sizeof buf) != 0) buf[0] = '\0';
|
|
3506
|
+
#endif
|
|
3503
3507
|
buf[sizeof buf - 1] = '\0';
|
|
3504
3508
|
return JS_NewString(ctx, buf);
|
|
3505
3509
|
}
|