@ai-ecoverse/slicc-kernel 1.31.1 → 1.31.2
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 +1 -1
- package/dist/kernel-worker.js +66 -12
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-process-worker.js +23 -6
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +66 -12
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +23 -6
- package/dist/process-worker.js.map +3 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -325,7 +325,7 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
|
|
|
325
325
|
- `@ai-ecoverse/slicc-kernel/driver` exports the types and `fsError`.
|
|
326
326
|
- **Capabilities:** `readonly`, `symlinks`, `chmod`, `ranges`, `maxIo` (the largest read or write per message), `maxFile`, `listingStats`, `linkTimes`, and `attrTtl`/`entryTtl` (in ms, default 1000). `linkTimes` promises that `setattr` with `mtime` on a symlink sets the link's own time. Without it, setting a link's own times (`touch -h`, `utimensat` with `AT_SYMLINK_NOFOLLOW`) fails with `EOPNOTSUPP` and never reaches the target. tmpfs has it; hostfs does not. Setting times through a link follows it in the kernel, so `setattr` gets the target. `ranges` (driver protocol 1.1) promises that a write lands in the file at once, with no copy of the whole file on `open` or `release`, and that `setattr` takes `size`.
|
|
327
327
|
- **Caching:** the kernel caches attributes and listings for those TTLs, and a driver can push `invalidate` for changes made outside.
|
|
328
|
-
- **File contents:** on a mount with `ranges`, as `tmpfs` is, and on the root file system when it is in OPFS (in a browser with sync access handles) or an in-memory root, a program reads and writes a file in 64 KiB pages: reads come from the driver 1 MiB at a time, and changed pages go back in 1 MiB runs once 8 MiB have changed, at `fsync`, and when the file is closed (from the kernel's own descriptors also 250 ms after a write). A file of any size the mount can hold works, with memory to spare for about one more copy of it. In OPFS a write opens a sync access handle for just that write. Elsewhere (`fsa` mounts, `hostfs` with an older proxy, and a root outside OPFS), file contents are read whole when a program opens a file and written back when it closes it. Both give close-to-open consistency, as NFS has.
|
|
328
|
+
- **File contents:** on a mount with `ranges`, as `tmpfs` is, and on the root file system when it is in OPFS (in a browser with sync access handles) or an in-memory root, a program reads and writes a file in 64 KiB pages: reads come from the driver 1 MiB at a time, and changed pages go back in 1 MiB runs once 8 MiB have changed, at `fsync`, and when the file is closed (from the kernel's own descriptors also 250 ms after a write). A file of any size the mount can hold works, with memory to spare for about one more copy of it. In OPFS a write opens a sync access handle for just that write. Elsewhere (`fsa` mounts, `hostfs` with an older proxy, and a root outside OPFS), file contents are read whole when a program opens a file and written back when it closes it. Both give close-to-open consistency, as NFS has. A process that ends without closing its files keeps what it wrote: on exit, on a trap, and when a signal kills it. For a killed WASI program, the kernel lets its worker write its changed pages back before `waitpid` reports the death. The only exception is a worker that makes no call into the kernel for 2 seconds after the signal; it is stopped without that write-back.
|
|
329
329
|
- **Rules on a mount:**
|
|
330
330
|
- a rename across mounts is `EXDEV`, so `mv` copies;
|
|
331
331
|
- `df` reports each mount from its driver's `statfs`;
|
package/dist/kernel-worker.js
CHANGED
|
@@ -5976,6 +5976,7 @@ var SAB_I_TIMERS = 9;
|
|
|
5976
5976
|
var SAB_I_MEMORY = 10;
|
|
5977
5977
|
var SAB_I_TOTAL_HI = 11;
|
|
5978
5978
|
var SAB_I_OFFSET_HI = 12;
|
|
5979
|
+
var SAB_I_KILLED = 13;
|
|
5979
5980
|
var U32 = 2 ** 32;
|
|
5980
5981
|
function storeU53(header2, lo, hi, value) {
|
|
5981
5982
|
Atomics.store(header2, hi, Math.floor(value / U32));
|
|
@@ -7817,6 +7818,7 @@ function isWasmSyscall(req) {
|
|
|
7817
7818
|
return typeof op === "string" && SYSCALL_OPS.has(op);
|
|
7818
7819
|
}
|
|
7819
7820
|
var MAX_READ = 1024 * 1024;
|
|
7821
|
+
var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
|
|
7820
7822
|
var HOSTS_ONLY = new Resolver({ routes: new Routes() });
|
|
7821
7823
|
var WasmProcess = class {
|
|
7822
7824
|
exited = false;
|
|
@@ -7825,6 +7827,7 @@ var WasmProcess = class {
|
|
|
7825
7827
|
ignored = 0;
|
|
7826
7828
|
inherited = 0;
|
|
7827
7829
|
interrupt = new AbortController();
|
|
7830
|
+
dying = false;
|
|
7828
7831
|
execChild;
|
|
7829
7832
|
execTermsig;
|
|
7830
7833
|
stopped = 0;
|
|
@@ -7857,7 +7860,10 @@ var WasmProcess = class {
|
|
|
7857
7860
|
signal(sig) {
|
|
7858
7861
|
if (this.execChild !== void 0) {
|
|
7859
7862
|
void Promise.resolve(this.options.kill?.(this.execChild, sig)).catch(() => void 0);
|
|
7860
|
-
|
|
7863
|
+
if (sig !== SIG.KILL) return "forward";
|
|
7864
|
+
if (!this.options.writesBack?.(this.execChild)) return "terminate";
|
|
7865
|
+
this.wake?.();
|
|
7866
|
+
return "forward";
|
|
7861
7867
|
}
|
|
7862
7868
|
if (sig === SIG.KILL) return "terminate";
|
|
7863
7869
|
if (sig === SIG.CONT) this.cont();
|
|
@@ -7901,14 +7907,22 @@ var WasmProcess = class {
|
|
|
7901
7907
|
this.options.onSyscall?.(req);
|
|
7902
7908
|
for (; ; ) {
|
|
7903
7909
|
await this.resumed;
|
|
7910
|
+
if (this.dying) return DYING;
|
|
7904
7911
|
const stops = this.stops;
|
|
7905
7912
|
const result = await this.dispatch(req);
|
|
7913
|
+
if (this.dying) return DYING;
|
|
7906
7914
|
const restart = !result.ok && result.errno === "EINTR" && this.stops !== stops;
|
|
7907
7915
|
if (restart && !this.options.hasPending?.()) continue;
|
|
7908
7916
|
await this.resumed;
|
|
7909
7917
|
return result;
|
|
7910
7918
|
}
|
|
7911
7919
|
}
|
|
7920
|
+
die() {
|
|
7921
|
+
if (this.dying) return;
|
|
7922
|
+
this.dying = true;
|
|
7923
|
+
this.interrupt.abort();
|
|
7924
|
+
this.wake?.();
|
|
7925
|
+
}
|
|
7912
7926
|
async dispatch(req) {
|
|
7913
7927
|
try {
|
|
7914
7928
|
if (isFdSyscall(req)) return await this.fdSyscall(req);
|
|
@@ -8369,6 +8383,7 @@ var WASM_MAX_THREADS = 256;
|
|
|
8369
8383
|
|
|
8370
8384
|
// src/kernel/host.ts
|
|
8371
8385
|
var CRASHED = 70;
|
|
8386
|
+
var KILL_GRACE_MS = 2e3;
|
|
8372
8387
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
8373
8388
|
var nextDescId = 1;
|
|
8374
8389
|
function descId(file) {
|
|
@@ -8392,6 +8407,41 @@ function inheritedFds(fds) {
|
|
|
8392
8407
|
};
|
|
8393
8408
|
});
|
|
8394
8409
|
}
|
|
8410
|
+
var Dying = class {
|
|
8411
|
+
ending;
|
|
8412
|
+
timer;
|
|
8413
|
+
busy = 0;
|
|
8414
|
+
finish;
|
|
8415
|
+
start(code, sig, header2, process, finish) {
|
|
8416
|
+
if (this.ending) return;
|
|
8417
|
+
this.ending = { code, sig };
|
|
8418
|
+
this.finish = finish;
|
|
8419
|
+
this.arm();
|
|
8420
|
+
Atomics.store(header2, SAB_I_KILLED, code);
|
|
8421
|
+
process.die();
|
|
8422
|
+
}
|
|
8423
|
+
async serving(work) {
|
|
8424
|
+
this.busy++;
|
|
8425
|
+
clearTimeout(this.timer);
|
|
8426
|
+
try {
|
|
8427
|
+
return await work();
|
|
8428
|
+
} finally {
|
|
8429
|
+
this.busy--;
|
|
8430
|
+
this.arm();
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
arm() {
|
|
8434
|
+
clearTimeout(this.timer);
|
|
8435
|
+
if (this.busy === 0 && this.finish) this.timer = setTimeout(this.finish, KILL_GRACE_MS);
|
|
8436
|
+
}
|
|
8437
|
+
clear() {
|
|
8438
|
+
clearTimeout(this.timer);
|
|
8439
|
+
}
|
|
8440
|
+
status(reported) {
|
|
8441
|
+
if (this.ending) return [this.ending.code, this.ending.sig];
|
|
8442
|
+
return [typeof reported === "number" ? reported : CRASHED, void 0];
|
|
8443
|
+
}
|
|
8444
|
+
};
|
|
8395
8445
|
function spawnWasmProcess(opts) {
|
|
8396
8446
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
8397
8447
|
const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
@@ -8403,6 +8453,7 @@ function spawnWasmProcess(opts) {
|
|
|
8403
8453
|
forker: opts.forker,
|
|
8404
8454
|
fs: opts.fs,
|
|
8405
8455
|
kill: opts.kill,
|
|
8456
|
+
...opts.writesBack ? { writesBack: opts.writesBack } : {},
|
|
8406
8457
|
processes: opts.processes,
|
|
8407
8458
|
openFiles: opts.openFiles,
|
|
8408
8459
|
...opts.nodes ? { nodes: opts.nodes } : {},
|
|
@@ -8433,18 +8484,16 @@ function spawnWasmProcess(opts) {
|
|
|
8433
8484
|
renamed: (from, to) => holds.renamed(from, to)
|
|
8434
8485
|
});
|
|
8435
8486
|
const worker = opts.createWorker();
|
|
8436
|
-
const
|
|
8437
|
-
|
|
8438
|
-
return dispatchSyncFs(req);
|
|
8439
|
-
};
|
|
8487
|
+
const dying = new Dying();
|
|
8488
|
+
const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ? process.syscall(req) : dispatchSyncFs(req));
|
|
8440
8489
|
const responder = attachSyncSabResponder(worker, sab, token, { dispatch: dispatch2 });
|
|
8441
|
-
|
|
8442
|
-
const exited = new Promise((resolve) => settle3 = resolve);
|
|
8490
|
+
const { promise: exited, resolve: settle3 } = Promise.withResolvers();
|
|
8443
8491
|
let done4 = false;
|
|
8444
8492
|
let endedBy;
|
|
8445
8493
|
const finish = (code, sig) => {
|
|
8446
8494
|
if (done4) return;
|
|
8447
8495
|
done4 = true;
|
|
8496
|
+
dying.clear();
|
|
8448
8497
|
endedBy = sig ?? process.execTermsig;
|
|
8449
8498
|
worker.removeEventListener("message", onMessage);
|
|
8450
8499
|
worker.removeEventListener("error", onError);
|
|
@@ -8460,10 +8509,10 @@ function spawnWasmProcess(opts) {
|
|
|
8460
8509
|
};
|
|
8461
8510
|
const handle = (data) => {
|
|
8462
8511
|
if (data?.type === WASM_PROCESS_EXIT) {
|
|
8463
|
-
finish(
|
|
8512
|
+
finish(...dying.status(data.code));
|
|
8464
8513
|
} else if (data?.type === WASM_PROCESS_ERROR) {
|
|
8465
|
-
opts.onError?.(String(data.message));
|
|
8466
|
-
finish(CRASHED);
|
|
8514
|
+
if (!dying.ending) opts.onError?.(String(data.message));
|
|
8515
|
+
finish(...dying.status(CRASHED));
|
|
8467
8516
|
} else if (data?.type === WASM_THREAD_SPAWN && data.thread) {
|
|
8468
8517
|
spawnThread(data.thread);
|
|
8469
8518
|
}
|
|
@@ -8533,7 +8582,10 @@ function spawnWasmProcess(opts) {
|
|
|
8533
8582
|
};
|
|
8534
8583
|
worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
|
|
8535
8584
|
const signal = (sig) => {
|
|
8536
|
-
if (process.signal(sig)
|
|
8585
|
+
if (process.signal(sig) !== "terminate") return;
|
|
8586
|
+
const code = sig === SIG.KILL ? 137 : 128 + sig;
|
|
8587
|
+
if (opts.program.abi !== "wasi" || done4) finish(code, sig);
|
|
8588
|
+
else dying.start(code, sig, header2, process, () => finish(code, sig));
|
|
8537
8589
|
};
|
|
8538
8590
|
const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
|
|
8539
8591
|
return {
|
|
@@ -8544,7 +8596,8 @@ function spawnWasmProcess(opts) {
|
|
|
8544
8596
|
termsig: () => endedBy,
|
|
8545
8597
|
onState: (listener) => process.onState(listener),
|
|
8546
8598
|
memory: () => Atomics.load(header2, SAB_I_MEMORY) * 65536,
|
|
8547
|
-
ignoredSignals: (fork) => process.inheritable(fork)
|
|
8599
|
+
ignoredSignals: (fork) => process.inheritable(fork),
|
|
8600
|
+
writesBack: () => opts.program.abi === "wasi" && !done4
|
|
8548
8601
|
};
|
|
8549
8602
|
}
|
|
8550
8603
|
|
|
@@ -10273,6 +10326,7 @@ var Launcher = class {
|
|
|
10273
10326
|
spawner: this.spawner(pid, req.report),
|
|
10274
10327
|
forker: this.forker(pid, req),
|
|
10275
10328
|
kill: (target, sig) => this.kill(target, sig),
|
|
10329
|
+
writesBack: (target) => this.processes.get(target)?.writesBack() === true,
|
|
10276
10330
|
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
10277
10331
|
openFiles: this.openFiles,
|
|
10278
10332
|
nodes: this.nodes,
|