@ai-ecoverse/slicc-kernel 1.102.4 → 1.102.6
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/README.md +10 -2
- package/dist/kernel-worker.js +68 -8
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +68 -8
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -311,7 +311,7 @@ On top of preview1 a WASI program gets:
|
|
|
311
311
|
- `MAP_SHARED` mappings aren't shared between processes, so unnamed semaphores (`sem_init` with `pshared`) work only between a process's threads, and `multiprocessing.shared_memory` doesn't work.
|
|
312
312
|
- **Foreign calls** (libffi's WASIX backend, so Python's `ctypes`): `call_dynamic` calls a function through the program's indirect function table (a static module's exported `__indirect_function_table`, or the dynamic linker's), its arguments and results packed in memory one after another (`i32`/`f32` 4 bytes, `i64`/`f64` 8). With `strict` set, the buffers must fit the function exactly (else `EINVAL`, before the function is called); without it, missing arguments are zero (one cut short keeps the bytes given, with zeros above them) and results that do not fit are left out. A trap or exception of the callee goes through to the caller. JS cannot ask a function for its type, so the kernel reads the types from the module files (the program's and each side module's, once, when a program first calls through `call_dynamic`): a table slot that is empty, past the end, holds a function of no module the kernel knows of, or takes or returns a `v128`, gives `EINVAL`. For closures (callbacks such as `CFUNCTYPE`), a dynamically linked program gets table slots from `closure_allocate`, and `closure_free` gives them back for reuse and empties the slot in every thread; a static program gets `ENOTSUP`. `closure_prepare` puts a trampoline with the requested signature (any of `i32`, `i64`, `f32`, `f64`, `v128`) into such a slot, in every thread: each call stores its arguments below the stack pointer and calls the backing function as `backing(values, results, user_data)`, then returns what it wrote to `results`. The trampolines are small wasm modules made and compiled at run time, once per signature in each thread (an instance per closure), so a page whose Content Security Policy forbids compiling WebAssembly (no `'wasm-unsafe-eval'`) cannot run them; it cannot run the program either. A `fork` or a `longjmp` (both through Asyncify) inside a function reached through `call_dynamic` cannot unwind through it.
|
|
313
313
|
- **Job control:** `^Z`, `SIGTSTP`, `SIGTTIN` and `SIGTTOU` stop a WASIX program at once, unless it has a handler for them. A stopped program that is busy computing is stopped for job control (`jobs` shows it, `fg`, `bg` and `SIGCONT` work), but its worker keeps a core busy until its next system call, since a worker's running code cannot be frozen from outside. Agents should `kill` such a job rather than leave it stopped. An Emscripten program stops at its next kernel call as well, and at its next write, read or sleep that its glue answers without the kernel (a write to its own `/dev/null`, as in `yes > /dev/null`), so a stopped one keeps no core busy unless it computes without any of these calls. A stopped program doesn't move on while stopped: a `sleep` (or a blocked read) that would end meanwhile ends after `SIGCONT`. A call that the stop interrupted runs again after `SIGCONT`, as on Linux. One with a timeout (`select`, `poll`, `ppoll`, `pselect`, `nanosleep`, a timed semaphore wait) waits only for the time that's left, counted from when the kernel first got the call, so it still ends at its deadline. `epoll_wait` and `epoll_pwait` aren't run again: they fail with `EINTR`, even without a handler (signal(7)). A signal for such a program is also delivered at those glue-answered calls, so a handler runs in a loop that only writes to `/dev/null`. For the same reason, a signal with a handler or a terminating default (`^C`, `SIGTERM`) reaches a program that is busy computing only at its next system call, so a CPU-bound loop may not end on `^C`; `kill -9` always ends it. A pending signal is delivered at the program's next WASI or WASIX call, including the calls its worker answers without the kernel (`random_get`, `args_get`, …), except the exempt ones: `clock_time_get` (a tight loop of it measured up to 30% slower), `proc_exit`, `proc_exit2`, `thread_exit`, `thread_spawn_v2` and `wasi.thread-spawn` (wasi-threads), the calls that install handlers (`callback_signal`, `slicc.sigaction_set`), and calls made while Asyncify unwinds or rewinds the stack for `fork` or `longjmp`. A signal that arrives while a thread computes is thus delivered at its next call; when that call is a wait (`poll_oneoff` with a timeout or a descriptor, so `nanosleep` and `poll`/`select` too, `sigmask_poll`, or a futex wait), the handler has run during the call, so the wait ends at once: `EINTR` for a libc of -22 or later (an older one sees its clocks fire, as for any interruption), and a futex wait returns woken. A thread blocked in the kernel or waiting on a lock (`pthread_cond_wait`, a futex) is woken by a signal for it (a futex wait checks for signals every 50 ms, so each waiting thread wakes briefly that often), and `pthread_kill` runs the handler on the thread it names. A sleep, or a clock in `poll_oneoff`, never ends before the time asked (it can end later, never earlier). A signal that ends a sleep or a `poll`/`select` with a timeout makes the call fail with `EINTR` in a program whose libc says it is wasix-sysroot 2025.9.30-22 or later (its `slicc.libc` custom section). Older libcs turn that answer into `ENOTSUP` from `nanosleep` (up to -18), so for a program without the section the wait ends as if it had timed out, unless it imports `fd_fdflags_set` (then `EINTR`, as before).
|
|
314
|
-
- **Blocked signals** (`sigprocmask`, `pthread_sigmask`, `sigpending`): a program whose libc imports `slicc.sigmask_set(how, set_lo, set_hi, old_ptr)` and `slicc.sigpending_get(ptr)` (wasix-sysroot 2025.9.30-28 and later) has a blocked mask per thread. `how` is musl's `SIG_BLOCK` 0, `SIG_UNBLOCK` 1 or `SIG_SETMASK` 2, the set is the 64-bit `sigset_t` (bit `sig - 1`), and the old mask is written as a u64 at `old_ptr`; any other `how` is `EINVAL`. `SIGKILL` and `SIGSTOP` cannot be blocked. A blocked signal is not delivered, does not interrupt a sleep or a blocked call, and its default action (terminate or stop) waits: it stays pending, `sigpending` shows it (the thread's pending signals and the process's), and it is delivered when it is unblocked, before `sigmask_set` returns. A signal ignored with `SIG_IGN` is discarded even while blocked. A forked child (with the mask of the thread that forked), the program an `exec` starts and a spawned child keep the mask, all 64 bits of it, and so does a new thread; `/proc/<pid>/status` shows the main thread's as `SigBlk`. The mask is kept in a word of the worker's shared header, so changing it costs no kernel call. A program without the import (an older libc) has no mask and gets every signal at once, as before, and one it starts by `exec` with a mask set starts with none. A signal for the whole process goes to the main thread,
|
|
314
|
+
- **Blocked signals** (`sigprocmask`, `pthread_sigmask`, `sigpending`): a program whose libc imports `slicc.sigmask_set(how, set_lo, set_hi, old_ptr)` and `slicc.sigpending_get(ptr)` (wasix-sysroot 2025.9.30-28 and later) has a blocked mask per thread. `how` is musl's `SIG_BLOCK` 0, `SIG_UNBLOCK` 1 or `SIG_SETMASK` 2, the set is the 64-bit `sigset_t` (bit `sig - 1`), and the old mask is written as a u64 at `old_ptr`; any other `how` is `EINVAL`. `SIGKILL` and `SIGSTOP` cannot be blocked. A blocked signal is not delivered, does not interrupt a sleep or a blocked call, and its default action (terminate or stop) waits: it stays pending, `sigpending` shows it (the thread's pending signals and the process's), and it is delivered when it is unblocked, before `sigmask_set` returns. A signal ignored with `SIG_IGN` is discarded even while blocked. A forked child (with the mask of the thread that forked), the program an `exec` starts and a spawned child keep the mask, all 64 bits of it, and so does a new thread; `/proc/<pid>/status` shows the main thread's as `SigBlk`. The mask is kept in a word of the worker's shared header, so changing it costs no kernel call. A program without the import (an older libc) has no mask and gets every signal at once, as before, and one it starts by `exec` with a mask set starts with none. A signal for the whole process goes to the main thread, unless the main thread blocks it and another thread does not. Then, as on Linux, it goes to that thread: its handler runs there, or with no handler its default action ends (or stops) the whole process at once. So the "signal thread" pattern works: block in main, unblock in a worker (#520). It waits on the main thread only when every thread blocks it. A signal sent to one thread (`pthread_kill`) that it blocks waits for that thread, and if it has no handler, the thread's unblock ends the whole process by the signal, whatever the other threads block. `slicc.sigmask_poll(set_lo, set_hi, in_ptr, out_ptr, nsubscriptions, nevents_ptr)` is `pselect`, `ppoll` and `sigsuspend`: it swaps the thread's mask to the given set for the call and puts the old one back before it returns. It delivers a pending signal the new mask unblocks, without polling, or polls as `poll_oneoff` does, under the new mask, so a signal it unblocks interrupts the wait with no window between the swap and the wait; with no subscriptions it waits until a signal is delivered. An interrupted call returns `EINTR` with no events, after the handler has run.
|
|
315
315
|
- **Pipes** have no file type for WASI and WASIX programs' `fstat` (`st_mode` type 0), because WASI has no FIFO file type. A socket type, as kernels 1.47.3 to 1.48.1 gave them, made Ruby treat a `popen` pipe as a socket (`close_write` then never ends the child's input). The kernel's `slicc_fs.fd_mode` answers `S_IFIFO | 0600` (Linux numbering) for a pipe, and wasix-sysroot 2025.9.30-21 and later take the type from it, so programs built on it see a FIFO (`S_ISFIFO`); older libcs keep only the permission bits. Until then Python's asyncio refuses a pipe for a subprocess (`Pipe transport is only for pipes, sockets and character devices`). They are read and written as pipes; socket calls on them fail with `ENOTSOCK`, and `isatty()` is false. Emscripten programs, bash included, see a FIFO. The same holds for any kernel descriptor that is neither seekable nor a terminal or device, such as captured output.
|
|
316
316
|
- **Sockets**: the WASIX socket calls (`sock_open`, `sock_pair` for an `AF_UNIX` stream pair, `bind`, `listen`, `connect`, `accept`, options, local and peer names) are the kernel's loopback sockets, and preview1's `sock_recv` / `sock_send` / `sock_shutdown` work on them and on inherited ones. Writing to a socket whose peer is gone fails with `EPIPE`, through `fd_write` as through `sock_send`, as if `MSG_NOSIGNAL` were set, since a WASI program cannot ignore `SIGPIPE`; a pipe or terminal whose reader is gone still ends the program with status 141. HTTP and HTTPS leave through the realm proxy that `https_proxy` names, as for any other program; `resolve` answers `/etc/hosts` names and literal addresses, and the [uplink](#uplink)'s names. `sock_open(SOCK_DGRAM)` on `AF_INET`/`AF_INET6` gives a [datagram socket](#datagram-sockets-udp); `sock_send_to` and `sock_recv_from` carry its addresses, and `RECV_DATA_TRUNCATED` is set in `ro_flags` when a datagram did not fit.
|
|
317
317
|
- **Diagnostics**: a trap ends the program with 134 and its message on stderr; with `SLICC_WASM_BACKTRACE=1` the wasm frames follow, named from the module's name section, or from a sidecar for a module shipped without one (`<module>.names` beside it, or the same path in a `<package>-names` package). `SLICC_WASI_STATS=1` makes a program print its calls, counted and timed, as it ends.
|
|
@@ -389,7 +389,7 @@ const kernel = await createKernel({ network: { transport: fetchTransport() } });
|
|
|
389
389
|
await kernel.run(['curl', '-sS', 'https://registry.npmjs.org/@ai-ecoverse/wasm-bash/latest']);
|
|
390
390
|
```
|
|
391
391
|
|
|
392
|
-
- **Sockets**: `AF_INET` stream sockets on `127.x` and `AF_UNIX` sockets are kernel descriptors on one loopback network per kernel, so processes can serve and connect to each other. They survive `dup`, `fork` and `exec` and work with `select`, `poll` and WASIX epoll, like pipe ends. A bind of an `AF_UNIX` socket creates its path in OPFS. Other addresses are unreachable (`ENETUNREACH`) unless the [uplink](#uplink) routes them: otherwise programs reach the outside world only through the proxy.
|
|
392
|
+
- **Sockets**: `AF_INET` stream sockets on `127.x` and `AF_UNIX` sockets are kernel descriptors on one loopback network per kernel, so processes can serve and connect to each other. They survive `dup`, `fork` and `exec` and work with `select`, `poll` and WASIX epoll, like pipe ends. A bind of an `AF_UNIX` socket creates its path in OPFS. A program's `connect` to a listener whose accept queue is full waits for a slot, as on Linux, instead of failing. A blocking connect waits, and a nonblocking one answers `EINPROGRESS` and turns writable once it's queued. After 30 s it fails with `ETIMEDOUT`, and it's `ECONNREFUSED` if the listener closes. Other addresses are unreachable (`ENETUNREACH`) unless the [uplink](#uplink) routes them: otherwise programs reach the outside world only through the proxy.
|
|
393
393
|
|
|
394
394
|
#### Datagram sockets (UDP)
|
|
395
395
|
|
|
@@ -1032,6 +1032,14 @@ A WASIX C program for `test/integration/stop-restart.test.mjs` (#738). `stopwait
|
|
|
1032
1032
|
|
|
1033
1033
|
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 (legacy exceptions) and binaryen's `wasm-opt` (`--asyncify` for `proc_fork`) on `PATH`. `bin/stopwait.wasm` is 59,958 bytes, sha256 `ce49e04baed13b7ccf04d8231da4a29567fde0dac19d5cc81f36c185e0eb4555`.
|
|
1034
1034
|
|
|
1035
|
+
### `sigthread`
|
|
1036
|
+
|
|
1037
|
+
A threaded WASIX C program for `test/integration/thread-signal.test.mjs` (#520). The main thread blocks `SIGUSR1` and `SIGTERM`, and a worker thread unblocks both. Then the program signals itself:
|
|
1038
|
+
- `sigthread handler` sends `SIGUSR1` (with a handler) and prints which thread ran the handler;
|
|
1039
|
+
- `sigthread default` sends `SIGTERM` (no handler), which must end the process.
|
|
1040
|
+
|
|
1041
|
+
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 (`-pthread`) and binaryen's `wasm-opt` on `PATH`. `bin/sigthread.wasm` is 44,788 bytes, sha256 `5781fa39d0a0032cd9b214b71de1f15f589b40e4cecd6862362048dd27f0db56`.
|
|
1042
|
+
|
|
1035
1043
|
### `longwait`
|
|
1036
1044
|
|
|
1037
1045
|
A WASIX C program for `test/integration/long-timers.test.mjs` (#767).
|
package/dist/kernel-worker.js
CHANGED
|
@@ -3564,6 +3564,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
3564
3564
|
backlog = 0;
|
|
3565
3565
|
boundAt;
|
|
3566
3566
|
changes = new Changes();
|
|
3567
|
+
room = new Changes();
|
|
3567
3568
|
options = /* @__PURE__ */ new Map();
|
|
3568
3569
|
constructor(net, domain) {
|
|
3569
3570
|
this.net = net;
|
|
@@ -3648,7 +3649,10 @@ var KernelSocket = class _KernelSocket {
|
|
|
3648
3649
|
for (; ; ) {
|
|
3649
3650
|
if (this.state !== "listening") throw new KernelError("EINVAL");
|
|
3650
3651
|
const next2 = this.queue.shift();
|
|
3651
|
-
if (next2)
|
|
3652
|
+
if (next2) {
|
|
3653
|
+
this.room.wake();
|
|
3654
|
+
return next2;
|
|
3655
|
+
}
|
|
3652
3656
|
if (nonblock) throw new KernelError("EAGAIN");
|
|
3653
3657
|
await this.changes.wait(signal);
|
|
3654
3658
|
}
|
|
@@ -3659,7 +3663,13 @@ var KernelSocket = class _KernelSocket {
|
|
|
3659
3663
|
this.changes.wake();
|
|
3660
3664
|
return true;
|
|
3661
3665
|
}
|
|
3662
|
-
|
|
3666
|
+
get full() {
|
|
3667
|
+
return this.state === "listening" && this.queue.length >= this.backlog;
|
|
3668
|
+
}
|
|
3669
|
+
waitRoom(signal) {
|
|
3670
|
+
return this.room.wait(signal);
|
|
3671
|
+
}
|
|
3672
|
+
connect(addr, viaUplink = true, cred, stamp, waits = true) {
|
|
3663
3673
|
if (this.state === "connected") throw new KernelError("EISCONN");
|
|
3664
3674
|
if (this.state === "connecting") throw new KernelError("EALREADY");
|
|
3665
3675
|
if (this.state !== "open") throw new KernelError("EINVAL");
|
|
@@ -3674,6 +3684,11 @@ var KernelSocket = class _KernelSocket {
|
|
|
3674
3684
|
this.boundAt = this.local;
|
|
3675
3685
|
} else this.local = { family: "unix", path: "" };
|
|
3676
3686
|
}
|
|
3687
|
+
if (waits && target.full) return this.queued(addr, target, cred, stamp);
|
|
3688
|
+
this.join(addr, target, cred, stamp);
|
|
3689
|
+
return void 0;
|
|
3690
|
+
}
|
|
3691
|
+
join(addr, target, cred, stamp) {
|
|
3677
3692
|
const server = new _KernelSocket(this.net, this.domain);
|
|
3678
3693
|
server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
|
|
3679
3694
|
server.peer = this.local;
|
|
@@ -3687,7 +3702,34 @@ var KernelSocket = class _KernelSocket {
|
|
|
3687
3702
|
server.close();
|
|
3688
3703
|
throw new KernelError("ECONNREFUSED");
|
|
3689
3704
|
}
|
|
3690
|
-
|
|
3705
|
+
}
|
|
3706
|
+
async queued(addr, target, cred, stamp) {
|
|
3707
|
+
this.state = "connecting";
|
|
3708
|
+
const abort = new AbortController();
|
|
3709
|
+
this.dialing = abort;
|
|
3710
|
+
const timer = setTimeout(
|
|
3711
|
+
() => abort.abort(new KernelError("ETIMEDOUT")),
|
|
3712
|
+
this.net.backlogWaitMs
|
|
3713
|
+
);
|
|
3714
|
+
try {
|
|
3715
|
+
while (target.full) await target.waitRoom(abort.signal);
|
|
3716
|
+
if (abort.signal.aborted) throw abort.signal.reason;
|
|
3717
|
+
this.state = "open";
|
|
3718
|
+
this.join(addr, target, cred, stamp);
|
|
3719
|
+
this.dialing = void 0;
|
|
3720
|
+
} catch (e) {
|
|
3721
|
+
const reason = abort.signal.aborted ? abort.signal.reason : e;
|
|
3722
|
+
const error = reason instanceof KernelError ? reason : new KernelError("ECONNREFUSED");
|
|
3723
|
+
if (this.dialing === abort) {
|
|
3724
|
+
this.dialing = void 0;
|
|
3725
|
+
this.failed = error.code;
|
|
3726
|
+
if (this.state === "connecting") this.state = "open";
|
|
3727
|
+
this.changes.wake();
|
|
3728
|
+
}
|
|
3729
|
+
throw error;
|
|
3730
|
+
} finally {
|
|
3731
|
+
clearTimeout(timer);
|
|
3732
|
+
}
|
|
3691
3733
|
}
|
|
3692
3734
|
dial(addr, route) {
|
|
3693
3735
|
this.state = "connecting";
|
|
@@ -3788,6 +3830,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
3788
3830
|
this.dialing = void 0;
|
|
3789
3831
|
if (was === "listening") {
|
|
3790
3832
|
for (const pending of this.queue.splice(0)) pending.close();
|
|
3833
|
+
this.room.wake();
|
|
3791
3834
|
}
|
|
3792
3835
|
if (this.link) {
|
|
3793
3836
|
if (!this.readShut) this.link.rx.closeRead();
|
|
@@ -3885,9 +3928,10 @@ var LoopbackNet = class {
|
|
|
3885
3928
|
socket.listen(backlog);
|
|
3886
3929
|
return socket;
|
|
3887
3930
|
}
|
|
3931
|
+
backlogWaitMs = DIAL_TIMEOUT_MS;
|
|
3888
3932
|
connect(addr) {
|
|
3889
3933
|
const socket = this.socket(addr.family);
|
|
3890
|
-
void socket.connect(addr, false);
|
|
3934
|
+
void socket.connect(addr, false, void 0, void 0, false);
|
|
3891
3935
|
return socket;
|
|
3892
3936
|
}
|
|
3893
3937
|
bind(socket, addr, kernel = false) {
|
|
@@ -12140,7 +12184,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
|
|
|
12140
12184
|
var EARLY_WHOLE_FILE = 64 * 1024;
|
|
12141
12185
|
|
|
12142
12186
|
// src/version.ts
|
|
12143
|
-
var KERNEL_VERSION = true ? "1.102.
|
|
12187
|
+
var KERNEL_VERSION = true ? "1.102.6" : "0.0.0-dev";
|
|
12144
12188
|
var OS_RELEASE = "6.0.0-slicc";
|
|
12145
12189
|
function procVersion() {
|
|
12146
12190
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -13395,7 +13439,8 @@ var KernelLoopback = class {
|
|
|
13395
13439
|
{ family: "inet", host: "127.0.0.1", port },
|
|
13396
13440
|
false,
|
|
13397
13441
|
this.cred(),
|
|
13398
|
-
this.stamp ? await this.stamp() : void 0
|
|
13442
|
+
this.stamp ? await this.stamp() : void 0,
|
|
13443
|
+
false
|
|
13399
13444
|
);
|
|
13400
13445
|
} catch (err) {
|
|
13401
13446
|
socket.close();
|
|
@@ -15951,8 +15996,15 @@ var WasmProcess = class {
|
|
|
15951
15996
|
const bit = sigbit(sig);
|
|
15952
15997
|
if (this.ignores(bit)) return "ignore";
|
|
15953
15998
|
if (((this.options.blockedBits?.() ?? 0) & bit) !== 0) {
|
|
15954
|
-
this.options.
|
|
15955
|
-
|
|
15999
|
+
const tid = this.options.unblockedThread?.(sig);
|
|
16000
|
+
if (tid === void 0) {
|
|
16001
|
+
this.options.onPending?.(sig);
|
|
16002
|
+
return "deliver";
|
|
16003
|
+
}
|
|
16004
|
+
if (this.caught & bit) {
|
|
16005
|
+
this.options.threadKill?.(tid, sig);
|
|
16006
|
+
return "deliver";
|
|
16007
|
+
}
|
|
15956
16008
|
}
|
|
15957
16009
|
if (!(this.caught & bit)) {
|
|
15958
16010
|
const action = defaultAction(sig);
|
|
@@ -17195,6 +17247,12 @@ function killThread(thread, caught, sig) {
|
|
|
17195
17247
|
blocked2.abort();
|
|
17196
17248
|
return true;
|
|
17197
17249
|
}
|
|
17250
|
+
function unblockedThread(threads, sig) {
|
|
17251
|
+
for (const [tid, thread] of threads) {
|
|
17252
|
+
if ((Atomics.load(thread.header, SAB_I_BLOCKED) & sigbit(sig)) === 0) return tid;
|
|
17253
|
+
}
|
|
17254
|
+
return void 0;
|
|
17255
|
+
}
|
|
17198
17256
|
function unblocked(header3) {
|
|
17199
17257
|
return Atomics.load(header3, SAB_I_SIGNALS) & ~Atomics.load(header3, SAB_I_BLOCKED);
|
|
17200
17258
|
}
|
|
@@ -17345,6 +17403,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
17345
17403
|
hasPending: () => unblocked(header3) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
|
|
17346
17404
|
raise: hooks.raise,
|
|
17347
17405
|
threadKill: hooks.threadKill,
|
|
17406
|
+
unblockedThread: hooks.unblockedThread,
|
|
17348
17407
|
...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
|
|
17349
17408
|
...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
|
|
17350
17409
|
...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {},
|
|
@@ -17385,6 +17444,7 @@ function spawnWasmProcess(opts) {
|
|
|
17385
17444
|
{
|
|
17386
17445
|
raise: (sig) => signal(sig),
|
|
17387
17446
|
threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig),
|
|
17447
|
+
unblockedThread: (sig) => unblockedThread(threadSignals, sig),
|
|
17388
17448
|
...opts.program.abi === "js" ? {
|
|
17389
17449
|
exitNow: (code) => exitNow(code),
|
|
17390
17450
|
exitSignal: (sig) => queueMicrotask(() => finish(128 + sig, sig))
|