@ai-ecoverse/slicc-kernel 1.122.3 → 1.123.0
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 +9 -0
- package/dist/kernel-worker.js +156 -12
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +24 -7
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +156 -12
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +24 -7
- package/dist/process-worker.js.map +2 -2
- package/dist/types/kernel/host.d.ts +2 -0
- package/dist/types/kernel/proc-info.d.ts +4 -0
- package/dist/types/kernel/protocol.d.ts +1 -0
- package/dist/types/kernel/wasm-memories.d.ts +20 -0
- package/dist/types/launcher.d.ts +6 -0
- package/dist/types/process/procfs.d.ts +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -918,6 +918,15 @@ Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `
|
|
|
918
918
|
|
|
919
919
|
The kernel writes `/etc/passwd` and `/etc/group` when it starts (see [Users](#users)). While the root has none of them, or no `/etc/mtab`, reading them gets per-process ones: root is uid 0 and the realm user, uid 1000, is named after `USER`, or `web_user` (Emscripten's default) without one, so it matches `$USER`, with `HOME` as its home, so `id`, `whoami`, `ls -l` and `ps` show names. Nothing is written to OPFS, and a real file there wins.
|
|
920
920
|
|
|
921
|
+
### How many processes at once
|
|
922
|
+
|
|
923
|
+
In a browser every process worker of the page shares one renderer. V8 reserves about 8 GiB of address space for each 32-bit wasm memory, whatever its declared maximum, and its sandbox holds about 1 TiB. So a page runs about 125 wasm processes (or programs that own a wasm memory) at once, counting everything in the renderer (#970). Node has no such ceiling, and neither does Linux. The kernel keeps processes inside that ceiling:
|
|
924
|
+
- **The ceiling:** 112 by default in a browser and none in Node. It's the kernel's count of live wasm processes, with the kernel's other wasm memories in the page left outside it. When an instantiation runs out of memory anyway, the kernel learns the real ceiling at that moment (the live count minus one), and that learned value wins over the default.
|
|
925
|
+
- **Admission:** a spawn, fork, exec or `kernel.run` at the ceiling waits for another process to exit. It keeps waiting as long as processes exit or are admitted, and fails with `EAGAIN` only after 10 s with neither. A long `make -j` or `xargs -P` therefore queues, and a pipeline that needs more live processes than the ceiling fails cleanly. bash's own retry on a fork's `EAGAIN` (`fork: retry: Resource temporarily unavailable`) handles the rest.
|
|
926
|
+
- **Close to the ceiling** (within 16 of it), the kernel waits for each new process to finish instantiating before the spawn returns. If it ran out of memory, the kernel starts it again, quietly, once there's room, so the caller sees no failure.
|
|
927
|
+
- **The rare residual case:** a process that fails to instantiate with nobody waiting on it, which happens only when something outside the count fills the renderer. It ends with status 126 and `<program>: cannot allocate memory for a new process (too many processes at once)` on its stderr, never a JS `RangeError`.
|
|
928
|
+
- **`/proc/sys/kernel/slicc_wasm_memories`** reads `<live> <ceiling>` (the ceiling 0 for none).
|
|
929
|
+
|
|
921
930
|
### Metadata
|
|
922
931
|
|
|
923
932
|
OPFS stores names, bytes, sizes and modification times, nothing else. Everything POSIX needs on top of that lives in an IndexedDB sidecar, so it survives reloads and new kernels:
|
package/dist/kernel-worker.js
CHANGED
|
@@ -6408,7 +6408,7 @@ function satisfies(version, range2) {
|
|
|
6408
6408
|
}
|
|
6409
6409
|
|
|
6410
6410
|
// src/version.ts
|
|
6411
|
-
var KERNEL_VERSION = true ? "1.
|
|
6411
|
+
var KERNEL_VERSION = true ? "1.123.0" : "0.0.0-dev";
|
|
6412
6412
|
var OS_RELEASE = "6.0.0-slicc";
|
|
6413
6413
|
function procVersion() {
|
|
6414
6414
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -12551,17 +12551,28 @@ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
|
|
|
12551
12551
|
var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
|
|
12552
12552
|
var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime", "version"];
|
|
12553
12553
|
var SYS_TREE = {
|
|
12554
|
-
kernel: {
|
|
12555
|
-
|
|
12554
|
+
kernel: {
|
|
12555
|
+
osrelease: `${OS_RELEASE}
|
|
12556
|
+
`,
|
|
12557
|
+
ostype: "Linux\n",
|
|
12558
|
+
slicc_wasm_memories: (listing) => `${listing.wasm?.live ?? 0} ${listing.wasm?.ceiling ?? 0}
|
|
12559
|
+
`
|
|
12560
|
+
}
|
|
12556
12561
|
};
|
|
12562
|
+
function isSysFile(at) {
|
|
12563
|
+
return typeof at === "string" || typeof at === "function";
|
|
12564
|
+
}
|
|
12557
12565
|
function sysEntry(parts2) {
|
|
12558
12566
|
let at = SYS_TREE;
|
|
12559
12567
|
for (const part of parts2) {
|
|
12560
|
-
if (
|
|
12568
|
+
if (isSysFile(at) || !Object.hasOwn(at, part)) return void 0;
|
|
12561
12569
|
at = at[part];
|
|
12562
12570
|
}
|
|
12563
12571
|
return at;
|
|
12564
12572
|
}
|
|
12573
|
+
function sysText(file, listing) {
|
|
12574
|
+
return typeof file === "string" ? file : file(listing());
|
|
12575
|
+
}
|
|
12565
12576
|
function commOf(info) {
|
|
12566
12577
|
const name2 = info.argv[0] ?? "";
|
|
12567
12578
|
return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
|
|
@@ -12797,7 +12808,9 @@ var ProcTree = class {
|
|
|
12797
12808
|
}
|
|
12798
12809
|
if (top === "sys") {
|
|
12799
12810
|
const entry = sysEntry(parts2.slice(1));
|
|
12800
|
-
if (typeof entry === "string"
|
|
12811
|
+
if (typeof entry === "string" || typeof entry === "function") {
|
|
12812
|
+
return { kind: "file", content: () => sysText(entry, () => this.listing()) };
|
|
12813
|
+
}
|
|
12801
12814
|
return entry && { kind: "dir", names: () => Object.keys(entry) };
|
|
12802
12815
|
}
|
|
12803
12816
|
if (top === "self") {
|
|
@@ -19254,6 +19267,7 @@ var WasmProcess = class {
|
|
|
19254
19267
|
var WASM_PROCESS_INIT = "wasm-process-init";
|
|
19255
19268
|
var WASM_PROCESS_EXIT = "wasm-process-exit";
|
|
19256
19269
|
var WASM_PROCESS_ERROR = "wasm-process-error";
|
|
19270
|
+
var WASM_PROCESS_READY = "wasm-process-ready";
|
|
19257
19271
|
var WASM_THREAD_SPAWN = "wasm-thread-spawn";
|
|
19258
19272
|
var WASM_THREAD_INIT = "wasm-thread-init";
|
|
19259
19273
|
var WASM_THREAD_EXIT = "wasm-thread-exit";
|
|
@@ -19261,6 +19275,8 @@ var WASM_MAX_THREADS = 256;
|
|
|
19261
19275
|
|
|
19262
19276
|
// src/kernel/host.ts
|
|
19263
19277
|
var CRASHED = 70;
|
|
19278
|
+
var NO_MEMORY = 126;
|
|
19279
|
+
var WASM_OOM = /Cannot allocate Wasm memory|WebAssembly\.Memory\(\): could not allocate memory/;
|
|
19264
19280
|
var KILL_GRACE_MS = 2e3;
|
|
19265
19281
|
var KILL_HARD_MS = 5e3;
|
|
19266
19282
|
var descIds = /* @__PURE__ */ new WeakMap();
|
|
@@ -19526,7 +19542,22 @@ function threadInit(opts, program, sab, cred, thread, main) {
|
|
|
19526
19542
|
function settleExit(exit, settle3, code) {
|
|
19527
19543
|
void exit.catch(() => void 0).then(() => settle3(code & 255));
|
|
19528
19544
|
}
|
|
19545
|
+
function readiness(abi) {
|
|
19546
|
+
const ready2 = Promise.withResolvers();
|
|
19547
|
+
if (abi === "js") ready2.resolve(true);
|
|
19548
|
+
return ready2;
|
|
19549
|
+
}
|
|
19550
|
+
function outOfMemory(opts, data) {
|
|
19551
|
+
if (data?.type !== WASM_PROCESS_ERROR || !WASM_OOM.test(String(data.message))) return false;
|
|
19552
|
+
if (!opts.onOom?.()) {
|
|
19553
|
+
opts.onError?.(
|
|
19554
|
+
`${opts.argv0}: cannot allocate memory for a new process (too many processes at once)`
|
|
19555
|
+
);
|
|
19556
|
+
}
|
|
19557
|
+
return true;
|
|
19558
|
+
}
|
|
19529
19559
|
function spawnWasmProcess(opts) {
|
|
19560
|
+
const { promise: ready2, resolve: started } = readiness(opts.program.abi);
|
|
19530
19561
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
19531
19562
|
const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
19532
19563
|
const blocked2 = opts.fork?.wasi?.blocked ?? opts.blocked;
|
|
@@ -19579,6 +19610,7 @@ function spawnWasmProcess(opts) {
|
|
|
19579
19610
|
const finish = (code, sig) => {
|
|
19580
19611
|
if (done4) return;
|
|
19581
19612
|
done4 = true;
|
|
19613
|
+
started(true);
|
|
19582
19614
|
clocks.freeze();
|
|
19583
19615
|
dying.clear();
|
|
19584
19616
|
endedBy = sig ?? process.execTermsig;
|
|
@@ -19596,7 +19628,12 @@ function spawnWasmProcess(opts) {
|
|
|
19596
19628
|
queueMicrotask(() => finish(...dying.status(code)));
|
|
19597
19629
|
};
|
|
19598
19630
|
const handle = (data) => {
|
|
19599
|
-
if (data?.type ===
|
|
19631
|
+
if (data?.type === WASM_PROCESS_READY) {
|
|
19632
|
+
started(true);
|
|
19633
|
+
} else if (outOfMemory(opts, data)) {
|
|
19634
|
+
started(false);
|
|
19635
|
+
finish(NO_MEMORY);
|
|
19636
|
+
} else if (data?.type === WASM_PROCESS_EXIT) {
|
|
19600
19637
|
finish(...dying.status(data.code));
|
|
19601
19638
|
} else if (data?.type === WASM_PROCESS_ERROR) {
|
|
19602
19639
|
if (!dying.ending) opts.onError?.(String(data.message));
|
|
@@ -19670,6 +19707,7 @@ function spawnWasmProcess(opts) {
|
|
|
19670
19707
|
return {
|
|
19671
19708
|
pid: opts.pid,
|
|
19672
19709
|
exited,
|
|
19710
|
+
ready: ready2,
|
|
19673
19711
|
kill,
|
|
19674
19712
|
signal,
|
|
19675
19713
|
termsig: () => endedBy,
|
|
@@ -21264,6 +21302,63 @@ var SettlingChildren = class {
|
|
|
21264
21302
|
}
|
|
21265
21303
|
};
|
|
21266
21304
|
|
|
21305
|
+
// src/kernel/wasm-memories.ts
|
|
21306
|
+
var DEFAULT_CEILING = 112;
|
|
21307
|
+
var IDLE_MS = 1e4;
|
|
21308
|
+
var MARGIN = 16;
|
|
21309
|
+
var WasmMemories = class {
|
|
21310
|
+
live = 0;
|
|
21311
|
+
ceiling;
|
|
21312
|
+
waiters = /* @__PURE__ */ new Set();
|
|
21313
|
+
idleMs;
|
|
21314
|
+
constructor(ceiling, idleMs = IDLE_MS) {
|
|
21315
|
+
this.ceiling = ceiling;
|
|
21316
|
+
this.idleMs = idleMs;
|
|
21317
|
+
}
|
|
21318
|
+
count() {
|
|
21319
|
+
return { live: this.live, ceiling: this.ceiling ?? 0 };
|
|
21320
|
+
}
|
|
21321
|
+
ended() {
|
|
21322
|
+
this.live = Math.max(0, this.live - 1);
|
|
21323
|
+
this.progress();
|
|
21324
|
+
}
|
|
21325
|
+
learn() {
|
|
21326
|
+
this.ceiling = Math.max(1, this.live - 1);
|
|
21327
|
+
}
|
|
21328
|
+
near() {
|
|
21329
|
+
return this.ceiling !== void 0 && this.live + MARGIN >= this.ceiling;
|
|
21330
|
+
}
|
|
21331
|
+
async admit() {
|
|
21332
|
+
while (this.ceiling !== void 0 && this.live >= this.ceiling) {
|
|
21333
|
+
if (!await this.changed()) throw new SpawnError("EAGAIN");
|
|
21334
|
+
}
|
|
21335
|
+
this.live += 1;
|
|
21336
|
+
this.progress();
|
|
21337
|
+
}
|
|
21338
|
+
progress() {
|
|
21339
|
+
const waiters = [...this.waiters];
|
|
21340
|
+
this.waiters.clear();
|
|
21341
|
+
for (const wake of waiters) wake();
|
|
21342
|
+
}
|
|
21343
|
+
changed() {
|
|
21344
|
+
return new Promise((resolve2) => {
|
|
21345
|
+
const wake = () => {
|
|
21346
|
+
clearTimeout(timer);
|
|
21347
|
+
resolve2(true);
|
|
21348
|
+
};
|
|
21349
|
+
const timer = setTimeout(() => {
|
|
21350
|
+
this.waiters.delete(wake);
|
|
21351
|
+
resolve2(false);
|
|
21352
|
+
}, this.idleMs);
|
|
21353
|
+
this.waiters.add(wake);
|
|
21354
|
+
});
|
|
21355
|
+
}
|
|
21356
|
+
};
|
|
21357
|
+
function defaultCeiling() {
|
|
21358
|
+
const node = globalThis.process?.versions?.node;
|
|
21359
|
+
return node === void 0 ? DEFAULT_CEILING : void 0;
|
|
21360
|
+
}
|
|
21361
|
+
|
|
21267
21362
|
// src/mount/driver.ts
|
|
21268
21363
|
function ownerOf2(req) {
|
|
21269
21364
|
const owner = {
|
|
@@ -23461,6 +23556,9 @@ function withScriptEnv(target, command) {
|
|
|
23461
23556
|
]
|
|
23462
23557
|
};
|
|
23463
23558
|
}
|
|
23559
|
+
function memoriesOf(given) {
|
|
23560
|
+
return new WasmMemories(given?.ceiling ?? defaultCeiling(), given?.idleMs);
|
|
23561
|
+
}
|
|
23464
23562
|
function childHandle(handle) {
|
|
23465
23563
|
return {
|
|
23466
23564
|
pid: handle.pid,
|
|
@@ -23532,6 +23630,7 @@ var Launcher = class {
|
|
|
23532
23630
|
jobs = new JobTable();
|
|
23533
23631
|
locks;
|
|
23534
23632
|
fifos = new FifoTable();
|
|
23633
|
+
memories;
|
|
23535
23634
|
sems = new SemTable();
|
|
23536
23635
|
settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
|
|
23537
23636
|
ptys = new PtyTable(
|
|
@@ -23585,6 +23684,7 @@ var Launcher = class {
|
|
|
23585
23684
|
terminals = 0;
|
|
23586
23685
|
constructor(options) {
|
|
23587
23686
|
this.usersMode = usersMode(options.users);
|
|
23687
|
+
this.memories = memoriesOf(options.wasmMemories);
|
|
23588
23688
|
this.rootFs = options.fs;
|
|
23589
23689
|
const peers = options.fs.peers?.();
|
|
23590
23690
|
this.locks = kernelLocks(options.fs.rootName, peers);
|
|
@@ -24415,7 +24515,11 @@ var Launcher = class {
|
|
|
24415
24515
|
}
|
|
24416
24516
|
const done4 = this.heldUrl(program);
|
|
24417
24517
|
try {
|
|
24418
|
-
const handle = this.
|
|
24518
|
+
const handle = await this.admitted(
|
|
24519
|
+
program.abi !== "js",
|
|
24520
|
+
req.fds,
|
|
24521
|
+
(fds, quietOom) => this.start({ ...req, fds, quietOom, cwd, program, argv0: target.argv0, args, env })
|
|
24522
|
+
);
|
|
24419
24523
|
void handle.exited.finally(done4);
|
|
24420
24524
|
return handle;
|
|
24421
24525
|
} catch (err) {
|
|
@@ -24459,6 +24563,39 @@ var Launcher = class {
|
|
|
24459
24563
|
if (req.tag !== void 0) this.tags.set(pid, req.tag);
|
|
24460
24564
|
if (req.links !== void 0) this.links.set(pid, req.links);
|
|
24461
24565
|
}
|
|
24566
|
+
async admitted(wasm, fds, start2) {
|
|
24567
|
+
if (!wasm) return start2(fds);
|
|
24568
|
+
for (; ; ) {
|
|
24569
|
+
await this.memories.admit().catch(async (err) => {
|
|
24570
|
+
await fds.closeAll();
|
|
24571
|
+
throw err;
|
|
24572
|
+
});
|
|
24573
|
+
const counted2 = (handle2) => {
|
|
24574
|
+
void handle2.exited.finally(() => this.memories.ended());
|
|
24575
|
+
return handle2;
|
|
24576
|
+
};
|
|
24577
|
+
if (!this.memories.near()) {
|
|
24578
|
+
try {
|
|
24579
|
+
return counted2(start2(fds));
|
|
24580
|
+
} catch (err) {
|
|
24581
|
+
this.memories.ended();
|
|
24582
|
+
throw err;
|
|
24583
|
+
}
|
|
24584
|
+
}
|
|
24585
|
+
const forked = fds.fork();
|
|
24586
|
+
const handle = await Promise.resolve().then(() => counted2(start2(forked, true))).catch(async (err) => {
|
|
24587
|
+
this.memories.ended();
|
|
24588
|
+
await forked.closeAll();
|
|
24589
|
+
throw err;
|
|
24590
|
+
});
|
|
24591
|
+
const fit = await (handle.ready ?? true);
|
|
24592
|
+
if (fit) {
|
|
24593
|
+
await fds.closeAll();
|
|
24594
|
+
return handle;
|
|
24595
|
+
}
|
|
24596
|
+
await handle.exited;
|
|
24597
|
+
}
|
|
24598
|
+
}
|
|
24462
24599
|
start(req) {
|
|
24463
24600
|
this.sampling();
|
|
24464
24601
|
if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
|
|
@@ -24509,6 +24646,10 @@ var Launcher = class {
|
|
|
24509
24646
|
...this.prefixes.length > 0 && !stack.writer ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
24510
24647
|
createWorker: this.createWorker,
|
|
24511
24648
|
onError: req.report,
|
|
24649
|
+
onOom: () => {
|
|
24650
|
+
this.memories.learn();
|
|
24651
|
+
return req.quietOom === true;
|
|
24652
|
+
},
|
|
24512
24653
|
spawner: this.spawner(pid, req.report),
|
|
24513
24654
|
forker: this.forker(pid, req),
|
|
24514
24655
|
kill: (target, sig, info) => this.kill(target, sig, this.credOf(pid), pid, info),
|
|
@@ -24517,7 +24658,8 @@ var Launcher = class {
|
|
|
24517
24658
|
boot: this.boot,
|
|
24518
24659
|
processes: this.list(this.credOf(pid)),
|
|
24519
24660
|
load: this.loadAverage(),
|
|
24520
|
-
cpu: this.cpuTotal()
|
|
24661
|
+
cpu: this.cpuTotal(),
|
|
24662
|
+
wasm: this.memories.count()
|
|
24521
24663
|
}),
|
|
24522
24664
|
sudo: (call, signal) => this.sudo(pid, call, signal),
|
|
24523
24665
|
crontab: (call) => this.crontab(pid, call),
|
|
@@ -24854,9 +24996,10 @@ var Launcher = class {
|
|
|
24854
24996
|
}
|
|
24855
24997
|
forker(ppid, parent) {
|
|
24856
24998
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
24857
|
-
return async (state,
|
|
24858
|
-
this.start({
|
|
24999
|
+
return async (state, given) => {
|
|
25000
|
+
const fresh2 = (fds, quietOom) => this.start({
|
|
24859
25001
|
...image,
|
|
25002
|
+
quietOom,
|
|
24860
25003
|
cwd: state.cwd ?? parent.cwd,
|
|
24861
25004
|
fds,
|
|
24862
25005
|
ppid,
|
|
@@ -24867,8 +25010,9 @@ var Launcher = class {
|
|
|
24867
25010
|
cred: this.credOf(ppid),
|
|
24868
25011
|
tag: this.tagOf(ppid),
|
|
24869
25012
|
links: this.links.get(ppid)
|
|
24870
|
-
})
|
|
24871
|
-
|
|
25013
|
+
});
|
|
25014
|
+
return childHandle(await this.admitted(parent.program.abi !== "js", given, fresh2));
|
|
25015
|
+
};
|
|
24872
25016
|
}
|
|
24873
25017
|
keySignal(tty, fallback, sig, send) {
|
|
24874
25018
|
const pgid = this.jobs.tcgetpgrp(tty, fallback);
|