@ai-ecoverse/slicc-kernel 1.54.2 → 1.54.4
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 +2 -2
- package/dist/kernel-worker.js +973 -486
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +247 -65
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +1073 -586
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +247 -65
- package/dist/process-worker.js.map +3 -3
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -1188,6 +1188,32 @@ function bytesSource(data) {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
});
|
|
1190
1190
|
}
|
|
1191
|
+
function memoryFile(data, path, position = 0) {
|
|
1192
|
+
let offset = position;
|
|
1193
|
+
return new OpenFile({
|
|
1194
|
+
read: async (max) => {
|
|
1195
|
+
const out = data.subarray(offset, offset + max);
|
|
1196
|
+
offset += out.length;
|
|
1197
|
+
return out;
|
|
1198
|
+
},
|
|
1199
|
+
pread: async (max, at) => data.subarray(at, at + max),
|
|
1200
|
+
seek: async (by, whence) => {
|
|
1201
|
+
const at = (whence === 1 ? offset : whence === 2 ? data.length : 0) + by;
|
|
1202
|
+
if (at < 0) throw new KernelError("EINVAL");
|
|
1203
|
+
offset = at;
|
|
1204
|
+
return at;
|
|
1205
|
+
},
|
|
1206
|
+
pwrite: async () => {
|
|
1207
|
+
throw new KernelError("EBADF");
|
|
1208
|
+
},
|
|
1209
|
+
resize: async () => {
|
|
1210
|
+
throw new KernelError("EBADF");
|
|
1211
|
+
},
|
|
1212
|
+
stat: async () => ({ path, size: data.length }),
|
|
1213
|
+
close: () => {
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1191
1217
|
function sinkFile(onData) {
|
|
1192
1218
|
return new OpenFile({
|
|
1193
1219
|
write: async (bytes) => {
|
|
@@ -7459,150 +7485,728 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7459
7485
|
};
|
|
7460
7486
|
}
|
|
7461
7487
|
|
|
7462
|
-
// src/
|
|
7463
|
-
var
|
|
7464
|
-
|
|
7465
|
-
"
|
|
7466
|
-
"
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
var json3 = (value) => ({ ok: true, kind: "json", json: value });
|
|
7475
|
-
var done2 = { ok: true, kind: "void" };
|
|
7476
|
-
var LockTable = class {
|
|
7477
|
-
held = /* @__PURE__ */ new Map();
|
|
7478
|
-
lock(pid, path, exclusive, fd) {
|
|
7479
|
-
const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
|
|
7480
|
-
for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
|
|
7481
|
-
const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
|
|
7482
|
-
mine.exclusive = exclusive;
|
|
7483
|
-
mine.fds.add(fd);
|
|
7484
|
-
holders.set(pid, mine);
|
|
7485
|
-
this.held.set(path, holders);
|
|
7486
|
-
return true;
|
|
7488
|
+
// src/process/process-fds.ts
|
|
7489
|
+
var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
|
|
7490
|
+
function mountTable(mounts = []) {
|
|
7491
|
+
const root = globalThis.navigator?.storage ? "opfs" : "memory";
|
|
7492
|
+
const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
7493
|
+
for (const m of mounts) {
|
|
7494
|
+
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
7495
|
+
if (m.state !== "ok") options.push(m.state);
|
|
7496
|
+
const access = options.includes("ro") ? [] : ["rw"];
|
|
7497
|
+
lines2.push(
|
|
7498
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
|
|
7499
|
+
);
|
|
7487
7500
|
}
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7501
|
+
return `${lines2.join("\n")}
|
|
7502
|
+
`;
|
|
7503
|
+
}
|
|
7504
|
+
|
|
7505
|
+
// src/process/procfs.ts
|
|
7506
|
+
var HZ = 100;
|
|
7507
|
+
var KIB = 1024;
|
|
7508
|
+
var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
|
|
7509
|
+
var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
|
|
7510
|
+
var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
|
|
7511
|
+
function commOf(info) {
|
|
7512
|
+
const name2 = info.argv[0] ?? "";
|
|
7513
|
+
return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
|
|
7514
|
+
}
|
|
7515
|
+
function ttyNumber(tty) {
|
|
7516
|
+
const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
|
|
7517
|
+
if (pts) return 136 << 8 | Number(pts[1]);
|
|
7518
|
+
const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
|
|
7519
|
+
return vt ? 4 << 8 | Number(vt[1]) : 0;
|
|
7520
|
+
}
|
|
7521
|
+
function pages(bytes) {
|
|
7522
|
+
return Math.ceil(bytes / 4096);
|
|
7523
|
+
}
|
|
7524
|
+
function ticks(ms) {
|
|
7525
|
+
return Math.max(0, Math.floor(ms * HZ / 1e3));
|
|
7526
|
+
}
|
|
7527
|
+
function pidStat(info, boot) {
|
|
7528
|
+
const fields = [
|
|
7529
|
+
info.pid,
|
|
7530
|
+
`(${commOf(info)})`,
|
|
7531
|
+
info.state,
|
|
7532
|
+
info.ppid,
|
|
7533
|
+
info.pgid,
|
|
7534
|
+
info.sid,
|
|
7535
|
+
ttyNumber(info.tty),
|
|
7536
|
+
info.tty ? info.pgid : -1,
|
|
7537
|
+
4194304,
|
|
7538
|
+
0,
|
|
7539
|
+
0,
|
|
7540
|
+
0,
|
|
7541
|
+
0,
|
|
7542
|
+
0,
|
|
7543
|
+
0,
|
|
7544
|
+
0,
|
|
7545
|
+
0,
|
|
7546
|
+
20,
|
|
7547
|
+
0,
|
|
7548
|
+
1,
|
|
7549
|
+
0,
|
|
7550
|
+
ticks(info.started - boot),
|
|
7551
|
+
info.memory,
|
|
7552
|
+
pages(info.memory),
|
|
7553
|
+
"18446744073709551615",
|
|
7554
|
+
...Array(12).fill(0),
|
|
7555
|
+
17,
|
|
7556
|
+
0,
|
|
7557
|
+
0,
|
|
7558
|
+
0,
|
|
7559
|
+
0,
|
|
7560
|
+
0,
|
|
7561
|
+
0,
|
|
7562
|
+
...Array(7).fill(0),
|
|
7563
|
+
0
|
|
7564
|
+
];
|
|
7565
|
+
return `${fields.join(" ")}
|
|
7566
|
+
`;
|
|
7567
|
+
}
|
|
7568
|
+
var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
|
|
7569
|
+
function pidStatus(info) {
|
|
7570
|
+
const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
|
|
7571
|
+
const c = info.cred ?? ROOT;
|
|
7572
|
+
const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
|
|
7573
|
+
return [
|
|
7574
|
+
`Name: ${commOf(info)}`,
|
|
7575
|
+
`Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
|
|
7576
|
+
`State: ${state}`,
|
|
7577
|
+
`Tgid: ${info.pid}`,
|
|
7578
|
+
"Ngid: 0",
|
|
7579
|
+
`Pid: ${info.pid}`,
|
|
7580
|
+
`PPid: ${info.ppid}`,
|
|
7581
|
+
"TracerPid: 0",
|
|
7582
|
+
`Uid: ${ids(c.ruid, c.euid, c.suid)}`,
|
|
7583
|
+
`Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
|
|
7584
|
+
"FDSize: 64",
|
|
7585
|
+
`VmSize: ${Math.ceil(info.memory / KIB)} kB`,
|
|
7586
|
+
`VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
|
|
7587
|
+
`Groups: ${c.groups.join(" ")}`,
|
|
7588
|
+
`NSpid: ${info.pid}`,
|
|
7589
|
+
`NSpgid: ${info.pgid}`,
|
|
7590
|
+
`NSsid: ${info.sid}`,
|
|
7591
|
+
"Threads: 1",
|
|
7592
|
+
"SigQ: 0/0",
|
|
7593
|
+
"SigPnd: 0000000000000000",
|
|
7594
|
+
"ShdPnd: 0000000000000000",
|
|
7595
|
+
"SigBlk: 0000000000000000",
|
|
7596
|
+
`SigIgn: ${sigMask(info.sigIgn)}`,
|
|
7597
|
+
`SigCgt: ${sigMask(info.sigCgt)}`,
|
|
7598
|
+
""
|
|
7599
|
+
].join("\n");
|
|
7600
|
+
}
|
|
7601
|
+
function pidFile(name2, info, boot) {
|
|
7602
|
+
if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
|
|
7603
|
+
if (name2 === "comm") return `${commOf(info)}
|
|
7604
|
+
`;
|
|
7605
|
+
if (name2 === "stat") return pidStat(info, boot);
|
|
7606
|
+
if (name2 === "statm") {
|
|
7607
|
+
const p = pages(info.memory);
|
|
7608
|
+
return `${p} ${p} 0 0 0 ${p} 0
|
|
7609
|
+
`;
|
|
7492
7610
|
}
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7611
|
+
return pidStatus(info);
|
|
7612
|
+
}
|
|
7613
|
+
function memoryInfo(source, used = 0) {
|
|
7614
|
+
const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
|
|
7615
|
+
const free = Math.max(0, total - used - (source.usedHeap ?? 0));
|
|
7616
|
+
const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
|
|
7617
|
+
return [
|
|
7618
|
+
["MemTotal", kb(total)],
|
|
7619
|
+
["MemFree", kb(free)],
|
|
7620
|
+
["MemAvailable", kb(free)],
|
|
7621
|
+
["Buffers", kb(0)],
|
|
7622
|
+
["Cached", kb(0)],
|
|
7623
|
+
["SwapCached", kb(0)],
|
|
7624
|
+
["Shmem", kb(0)],
|
|
7625
|
+
["SReclaimable", kb(0)],
|
|
7626
|
+
["SwapTotal", kb(0)],
|
|
7627
|
+
["SwapFree", kb(0)]
|
|
7628
|
+
].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
|
|
7629
|
+
}
|
|
7630
|
+
function systemFile(name2, listing, now, memory) {
|
|
7631
|
+
const up = Math.max(0, now - listing.boot) / 1e3;
|
|
7632
|
+
if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
|
|
7633
|
+
`;
|
|
7634
|
+
if (name2 === "meminfo") {
|
|
7635
|
+
return memoryInfo(
|
|
7636
|
+
memory,
|
|
7637
|
+
listing.processes.reduce((sum, p) => sum + p.memory, 0)
|
|
7638
|
+
);
|
|
7498
7639
|
}
|
|
7499
|
-
|
|
7500
|
-
|
|
7640
|
+
const live = listing.processes.filter((p) => p.state !== "Z");
|
|
7641
|
+
if (name2 === "loadavg") {
|
|
7642
|
+
const last = Math.max(0, ...listing.processes.map((p) => p.pid));
|
|
7643
|
+
return `0.00 0.00 0.00 1/${live.length} ${last}
|
|
7644
|
+
`;
|
|
7501
7645
|
}
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7646
|
+
const idle = ticks(now - listing.boot);
|
|
7647
|
+
return [
|
|
7648
|
+
`cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
|
|
7649
|
+
`cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
|
|
7650
|
+
"intr 0",
|
|
7651
|
+
"ctxt 0",
|
|
7652
|
+
`btime ${Math.floor(listing.boot / 1e3)}`,
|
|
7653
|
+
`processes ${listing.processes.length}`,
|
|
7654
|
+
"procs_running 1",
|
|
7655
|
+
"procs_blocked 0",
|
|
7656
|
+
""
|
|
7657
|
+
].join("\n");
|
|
7658
|
+
}
|
|
7659
|
+
function memorySource(scope = globalThis) {
|
|
7660
|
+
const nav = scope.navigator;
|
|
7661
|
+
const perf = scope.performance;
|
|
7662
|
+
const deviceMemory = nav?.deviceMemory;
|
|
7663
|
+
const used = perf?.memory?.usedJSHeapSize;
|
|
7664
|
+
return {
|
|
7665
|
+
...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
|
|
7666
|
+
...typeof used === "number" ? { usedHeap: used } : {}
|
|
7667
|
+
};
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
// src/fs/pages.ts
|
|
7671
|
+
var PAGE = 64 * 1024;
|
|
7672
|
+
var READAHEAD = 1024 * 1024;
|
|
7673
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
7674
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
7675
|
+
var RUN = 1024 * 1024;
|
|
7676
|
+
var Pages = class {
|
|
7677
|
+
length;
|
|
7678
|
+
stored;
|
|
7679
|
+
shrink;
|
|
7680
|
+
pages = /* @__PURE__ */ new Map();
|
|
7681
|
+
dirty = /* @__PURE__ */ new Set();
|
|
7682
|
+
free = [];
|
|
7683
|
+
pinned = false;
|
|
7684
|
+
constructor(size) {
|
|
7685
|
+
this.length = size;
|
|
7686
|
+
this.stored = size;
|
|
7515
7687
|
}
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
const running = new AbortController();
|
|
7519
|
-
this.running.set(id2, running);
|
|
7520
|
-
this.run(req, running.signal).then(
|
|
7521
|
-
(result) => this.complete(id2, result),
|
|
7522
|
-
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
7523
|
-
);
|
|
7524
|
-
return id2;
|
|
7688
|
+
get dirtyBytes() {
|
|
7689
|
+
return this.dirty.size * PAGE;
|
|
7525
7690
|
}
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
this.complete(id2, json3(value));
|
|
7529
|
-
return id2;
|
|
7691
|
+
get changed() {
|
|
7692
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
7530
7693
|
}
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
this.
|
|
7534
|
-
|
|
7694
|
+
missing(at, length2) {
|
|
7695
|
+
this.evict();
|
|
7696
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
7697
|
+
const runs = [];
|
|
7698
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
7699
|
+
if (this.pages.has(page)) continue;
|
|
7700
|
+
const last2 = runs[runs.length - 1];
|
|
7701
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
7702
|
+
else runs.push([page * PAGE, PAGE]);
|
|
7703
|
+
}
|
|
7704
|
+
const last = runs[runs.length - 1];
|
|
7705
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
7706
|
+
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
7535
7707
|
}
|
|
7536
|
-
|
|
7537
|
-
if (
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7708
|
+
partial(at, length2) {
|
|
7709
|
+
if (length2 === 0) return [];
|
|
7710
|
+
this.evict();
|
|
7711
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
7712
|
+
const runs = [];
|
|
7713
|
+
for (const page of edges) {
|
|
7714
|
+
const start2 = page * PAGE;
|
|
7715
|
+
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
7716
|
+
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
7717
|
+
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
7718
|
+
}
|
|
7719
|
+
return runs;
|
|
7542
7720
|
}
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7721
|
+
fill(start2, bytes) {
|
|
7722
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
7723
|
+
const page = (start2 + at) / PAGE;
|
|
7724
|
+
if (this.pages.has(page)) continue;
|
|
7725
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
7726
|
+
const fresh = this.take(part.length);
|
|
7727
|
+
fresh.set(part);
|
|
7728
|
+
this.pages.set(page, fresh);
|
|
7729
|
+
}
|
|
7546
7730
|
}
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
this.
|
|
7550
|
-
|
|
7551
|
-
if (at >= 0) this.ready.splice(at, 1);
|
|
7731
|
+
read(at, length2) {
|
|
7732
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
7733
|
+
this.readInto(at, out);
|
|
7734
|
+
return out;
|
|
7552
7735
|
}
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
const
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
const
|
|
7560
|
-
if (
|
|
7736
|
+
readInto(at, out) {
|
|
7737
|
+
const end = Math.min(this.length, at + out.length);
|
|
7738
|
+
for (let pos = at; pos < end; ) {
|
|
7739
|
+
const page = Math.floor(pos / PAGE);
|
|
7740
|
+
const from = pos - page * PAGE;
|
|
7741
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
7742
|
+
const bytes = this.pages.get(page);
|
|
7743
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
7744
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
7745
|
+
pos += n;
|
|
7561
7746
|
}
|
|
7747
|
+
return Math.max(0, end - at);
|
|
7562
7748
|
}
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
let
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
this.
|
|
7573
|
-
signal.removeEventListener("abort", abort);
|
|
7574
|
-
};
|
|
7575
|
-
const wake = () => {
|
|
7576
|
-
cleanup();
|
|
7577
|
-
resolve(true);
|
|
7578
|
-
};
|
|
7579
|
-
const abort = () => {
|
|
7580
|
-
cleanup();
|
|
7581
|
-
reject(new KernelError("EINTR"));
|
|
7582
|
-
};
|
|
7583
|
-
this.waiters.add(wake);
|
|
7584
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
7585
|
-
if (timeoutMs !== void 0) {
|
|
7586
|
-
timer = setTimeout(() => {
|
|
7587
|
-
cleanup();
|
|
7588
|
-
resolve(false);
|
|
7589
|
-
}, timeoutMs);
|
|
7749
|
+
write(at, bytes) {
|
|
7750
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
7751
|
+
const pos = at + done4;
|
|
7752
|
+
const page = Math.floor(pos / PAGE);
|
|
7753
|
+
const from = pos - page * PAGE;
|
|
7754
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
7755
|
+
let target = this.pages.get(page);
|
|
7756
|
+
if (!target) {
|
|
7757
|
+
target = this.take(0);
|
|
7758
|
+
this.pages.set(page, target);
|
|
7590
7759
|
}
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
this.results.set(id2, result);
|
|
7597
|
-
this.ready.push(id2);
|
|
7598
|
-
this.wake();
|
|
7599
|
-
this.onReady?.();
|
|
7760
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
7761
|
+
this.dirty.add(page);
|
|
7762
|
+
done4 += n;
|
|
7763
|
+
}
|
|
7764
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
7600
7765
|
}
|
|
7601
|
-
|
|
7602
|
-
|
|
7766
|
+
truncate(size) {
|
|
7767
|
+
if (size < this.stored) {
|
|
7768
|
+
this.stored = size;
|
|
7769
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
7770
|
+
}
|
|
7771
|
+
if (size < this.length) {
|
|
7772
|
+
const edge = Math.floor(size / PAGE);
|
|
7773
|
+
for (const page of [...this.pages.keys()]) {
|
|
7774
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
7775
|
+
}
|
|
7776
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
7777
|
+
}
|
|
7778
|
+
this.length = size;
|
|
7603
7779
|
}
|
|
7604
|
-
|
|
7605
|
-
|
|
7780
|
+
plan() {
|
|
7781
|
+
const runs = [];
|
|
7782
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
7783
|
+
for (let i = 0; i < sorted.length; ) {
|
|
7784
|
+
let j = i + 1;
|
|
7785
|
+
const first = sorted[i];
|
|
7786
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
7787
|
+
j++;
|
|
7788
|
+
}
|
|
7789
|
+
const start2 = first * PAGE;
|
|
7790
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
7791
|
+
const bytes = new Uint8Array(end - start2);
|
|
7792
|
+
for (let k = i; k < j; k++) {
|
|
7793
|
+
const page = this.pages.get(sorted[k]);
|
|
7794
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
|
|
7795
|
+
}
|
|
7796
|
+
runs.push({ offset: start2, bytes });
|
|
7797
|
+
i = j;
|
|
7798
|
+
}
|
|
7799
|
+
const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
7800
|
+
return {
|
|
7801
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
7802
|
+
runs,
|
|
7803
|
+
...this.length > written2 ? { extend: this.length } : {}
|
|
7804
|
+
};
|
|
7805
|
+
}
|
|
7806
|
+
flushed() {
|
|
7807
|
+
this.dirty.clear();
|
|
7808
|
+
this.shrink = void 0;
|
|
7809
|
+
this.stored = this.length;
|
|
7810
|
+
this.evict();
|
|
7811
|
+
}
|
|
7812
|
+
pin() {
|
|
7813
|
+
this.pinned = true;
|
|
7814
|
+
}
|
|
7815
|
+
reset(size) {
|
|
7816
|
+
if (this.pinned) return;
|
|
7817
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
7818
|
+
if (this.changed) return;
|
|
7819
|
+
this.length = size;
|
|
7820
|
+
this.stored = size;
|
|
7821
|
+
}
|
|
7822
|
+
evict() {
|
|
7823
|
+
if (this.pinned) return;
|
|
7824
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
7825
|
+
for (const page of this.pages.keys()) {
|
|
7826
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
7827
|
+
if (this.dirty.has(page)) continue;
|
|
7828
|
+
this.drop(page);
|
|
7829
|
+
clean -= PAGE;
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
take(used) {
|
|
7833
|
+
const page = this.free.pop();
|
|
7834
|
+
if (!page) return new Uint8Array(PAGE);
|
|
7835
|
+
page.fill(0, used);
|
|
7836
|
+
return page;
|
|
7837
|
+
}
|
|
7838
|
+
drop(page) {
|
|
7839
|
+
const bytes = this.pages.get(page);
|
|
7840
|
+
this.pages.delete(page);
|
|
7841
|
+
this.dirty.delete(page);
|
|
7842
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
7843
|
+
}
|
|
7844
|
+
};
|
|
7845
|
+
|
|
7846
|
+
// src/fs/ranged.ts
|
|
7847
|
+
var AsyncRangedFile = class {
|
|
7848
|
+
pages;
|
|
7849
|
+
io;
|
|
7850
|
+
pinned = false;
|
|
7851
|
+
version;
|
|
7852
|
+
path;
|
|
7853
|
+
constructor(io, path, size, version) {
|
|
7854
|
+
this.io = io;
|
|
7855
|
+
this.path = path;
|
|
7856
|
+
this.version = version;
|
|
7857
|
+
this.pages = new Pages(size);
|
|
7858
|
+
}
|
|
7859
|
+
size() {
|
|
7860
|
+
return this.pages.length;
|
|
7861
|
+
}
|
|
7862
|
+
async fetch(runs) {
|
|
7863
|
+
for (const [start2, n] of runs) {
|
|
7864
|
+
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
async read(at, length2) {
|
|
7868
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
7869
|
+
return this.pages.read(at, length2);
|
|
7870
|
+
}
|
|
7871
|
+
async write(at, bytes) {
|
|
7872
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
7873
|
+
this.pages.write(at, bytes);
|
|
7874
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
7875
|
+
}
|
|
7876
|
+
truncate(size) {
|
|
7877
|
+
this.pages.truncate(size);
|
|
7878
|
+
}
|
|
7879
|
+
async flush() {
|
|
7880
|
+
if (this.pinned || !this.pages.changed) return;
|
|
7881
|
+
const plan = this.pages.plan();
|
|
7882
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
7883
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
7884
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
7885
|
+
this.version = void 0;
|
|
7886
|
+
this.pages.flushed();
|
|
7887
|
+
}
|
|
7888
|
+
async pin() {
|
|
7889
|
+
this.pinned = true;
|
|
7890
|
+
this.pages.pin();
|
|
7891
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
7892
|
+
}
|
|
7893
|
+
};
|
|
7894
|
+
|
|
7895
|
+
// src/process/wasi/wasi-files.ts
|
|
7896
|
+
function normalize(path) {
|
|
7897
|
+
const out = [];
|
|
7898
|
+
for (const seg of path.split("/")) {
|
|
7899
|
+
if (seg === "" || seg === ".") continue;
|
|
7900
|
+
if (seg === "..") out.pop();
|
|
7901
|
+
else out.push(seg);
|
|
7902
|
+
}
|
|
7903
|
+
return `/${out.join("/")}`;
|
|
7904
|
+
}
|
|
7905
|
+
|
|
7906
|
+
// src/process/wasi/wasi-procfs.ts
|
|
7907
|
+
var DIR_MODE = 16749;
|
|
7908
|
+
var FILE_MODE = 33060;
|
|
7909
|
+
var LINK_MODE = 41471;
|
|
7910
|
+
var PID_NAMES = PID_FILES;
|
|
7911
|
+
var SYSTEM_NAMES = SYSTEM_FILES;
|
|
7912
|
+
function inProc(path) {
|
|
7913
|
+
return path === "/proc" || path.startsWith("/proc/");
|
|
7914
|
+
}
|
|
7915
|
+
var ProcTree = class {
|
|
7916
|
+
source;
|
|
7917
|
+
pid;
|
|
7918
|
+
memory;
|
|
7919
|
+
constructor(source, pid, memory = memorySource()) {
|
|
7920
|
+
this.source = source;
|
|
7921
|
+
this.pid = pid;
|
|
7922
|
+
this.memory = memory;
|
|
7923
|
+
}
|
|
7924
|
+
listing() {
|
|
7925
|
+
return this.source.procList();
|
|
7926
|
+
}
|
|
7927
|
+
alive(pid) {
|
|
7928
|
+
return this.listing().processes.some((p) => p.pid === pid);
|
|
7929
|
+
}
|
|
7930
|
+
node(parts) {
|
|
7931
|
+
const [top, leaf, ...rest2] = parts;
|
|
7932
|
+
if (top === void 0) {
|
|
7933
|
+
return {
|
|
7934
|
+
kind: "dir",
|
|
7935
|
+
names: () => [
|
|
7936
|
+
...SYSTEM_NAMES,
|
|
7937
|
+
"mounts",
|
|
7938
|
+
"self",
|
|
7939
|
+
...this.listing().processes.map((p) => String(p.pid))
|
|
7940
|
+
]
|
|
7941
|
+
};
|
|
7942
|
+
}
|
|
7943
|
+
if (top === "self") {
|
|
7944
|
+
return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
|
|
7945
|
+
}
|
|
7946
|
+
if (leaf === void 0 && top === "mounts") {
|
|
7947
|
+
return { kind: "file", content: () => mountTable(this.source.mountList()) };
|
|
7948
|
+
}
|
|
7949
|
+
if (leaf === void 0 && SYSTEM_NAMES.includes(top)) {
|
|
7950
|
+
return {
|
|
7951
|
+
kind: "file",
|
|
7952
|
+
content: () => systemFile(top, this.listing(), Date.now(), this.memory)
|
|
7953
|
+
};
|
|
7954
|
+
}
|
|
7955
|
+
if (!/^[1-9]\d*$/.test(top) || rest2.length > 0 || !this.alive(Number(top))) return void 0;
|
|
7956
|
+
if (leaf === void 0) return { kind: "dir", names: () => [...PID_NAMES] };
|
|
7957
|
+
if (!PID_NAMES.includes(leaf)) return void 0;
|
|
7958
|
+
return {
|
|
7959
|
+
kind: "file",
|
|
7960
|
+
content: () => {
|
|
7961
|
+
const listing = this.listing();
|
|
7962
|
+
const info = listing.processes.find((p) => p.pid === Number(top));
|
|
7963
|
+
if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
|
|
7964
|
+
return pidFile(leaf, info, listing.boot);
|
|
7965
|
+
}
|
|
7966
|
+
};
|
|
7967
|
+
}
|
|
7968
|
+
parts(path, follow2) {
|
|
7969
|
+
const parts = path.split("/").filter((p) => p !== "").slice(1);
|
|
7970
|
+
if (parts[0] === "self" && (follow2 || parts.length > 1)) parts[0] = String(this.pid);
|
|
7971
|
+
return parts;
|
|
7972
|
+
}
|
|
7973
|
+
find(path, follow2) {
|
|
7974
|
+
const node = this.node(this.parts(path, follow2));
|
|
7975
|
+
if (!node) throw syncError("ENOENT", path);
|
|
7976
|
+
return node;
|
|
7977
|
+
}
|
|
7978
|
+
stat(path, follow2 = true) {
|
|
7979
|
+
const node = this.find(path, follow2);
|
|
7980
|
+
const base = {
|
|
7981
|
+
isFile: false,
|
|
7982
|
+
isDirectory: false,
|
|
7983
|
+
size: 0,
|
|
7984
|
+
mtimeMs: Date.now(),
|
|
7985
|
+
readonly: true
|
|
7986
|
+
};
|
|
7987
|
+
if (node.kind === "dir") return { ...base, isDirectory: true, mode: DIR_MODE };
|
|
7988
|
+
if (node.kind === "link") {
|
|
7989
|
+
return { ...base, isSymbolicLink: true, mode: LINK_MODE, size: node.target.length };
|
|
7990
|
+
}
|
|
7991
|
+
return { ...base, isFile: true, mode: FILE_MODE, size: this.bytes(node).length };
|
|
7992
|
+
}
|
|
7993
|
+
bytes(node) {
|
|
7994
|
+
return new TextEncoder().encode(node.kind === "file" ? node.content() : "");
|
|
7995
|
+
}
|
|
7996
|
+
read(path) {
|
|
7997
|
+
const node = this.find(path, true);
|
|
7998
|
+
if (node.kind !== "file") throw syncError("EISDIR", path);
|
|
7999
|
+
return this.bytes(node);
|
|
8000
|
+
}
|
|
8001
|
+
readdir(path) {
|
|
8002
|
+
const node = this.find(path, true);
|
|
8003
|
+
if (node.kind !== "dir") throw syncError("ENOTDIR", path);
|
|
8004
|
+
return node.names();
|
|
8005
|
+
}
|
|
8006
|
+
readlink(path) {
|
|
8007
|
+
const node = this.find(path, false);
|
|
8008
|
+
if (node.kind !== "link") throw syncError("EINVAL", path);
|
|
8009
|
+
return node.target;
|
|
8010
|
+
}
|
|
8011
|
+
exists(path) {
|
|
8012
|
+
try {
|
|
8013
|
+
this.find(path, true);
|
|
8014
|
+
return true;
|
|
8015
|
+
} catch {
|
|
8016
|
+
return false;
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
};
|
|
8020
|
+
var MAX_HOPS2 = 40;
|
|
8021
|
+
function* procWalk(path, follow2) {
|
|
8022
|
+
let done4 = [];
|
|
8023
|
+
let todo = path.split("/").filter((part) => part !== "");
|
|
8024
|
+
let hops = 0;
|
|
8025
|
+
while (todo.length > 0) {
|
|
8026
|
+
const part = todo.shift();
|
|
8027
|
+
if (part === ".") continue;
|
|
8028
|
+
if (part === "..") {
|
|
8029
|
+
done4.pop();
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
const at = `/${[...done4, part].join("/")}`;
|
|
8033
|
+
const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
|
|
8034
|
+
if (inProc(at) && inProc(whole)) return whole;
|
|
8035
|
+
if (!follow2 && todo.length === 0) return void 0;
|
|
8036
|
+
const target = yield at;
|
|
8037
|
+
if (target === null) return void 0;
|
|
8038
|
+
if (target === void 0) done4.push(part);
|
|
8039
|
+
else {
|
|
8040
|
+
if (++hops > MAX_HOPS2) return void 0;
|
|
8041
|
+
if (target.startsWith("/")) done4 = [];
|
|
8042
|
+
todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
return void 0;
|
|
8046
|
+
}
|
|
8047
|
+
async function procAlias(fs, path, follow2) {
|
|
8048
|
+
const { readlink, lstat } = fs;
|
|
8049
|
+
if (!readlink || !lstat) return void 0;
|
|
8050
|
+
const physical = await lstat.call(fs, "/proc").then(
|
|
8051
|
+
() => true,
|
|
8052
|
+
() => false
|
|
8053
|
+
);
|
|
8054
|
+
if (!physical) return void 0;
|
|
8055
|
+
const walk = procWalk(path, follow2);
|
|
8056
|
+
let step = walk.next();
|
|
8057
|
+
while (!step.done) {
|
|
8058
|
+
const target = await readlink.call(fs, step.value).catch(() => void 0);
|
|
8059
|
+
step = walk.next(target);
|
|
8060
|
+
}
|
|
8061
|
+
return step.value;
|
|
8062
|
+
}
|
|
8063
|
+
|
|
8064
|
+
// src/kernel/host-ops.ts
|
|
8065
|
+
var HOST_OPS = [
|
|
8066
|
+
"lock",
|
|
8067
|
+
"unlock",
|
|
8068
|
+
"async-submit",
|
|
8069
|
+
"async-wait",
|
|
8070
|
+
"async-take",
|
|
8071
|
+
"async-resolve",
|
|
8072
|
+
"async-hold",
|
|
8073
|
+
"async-cancel",
|
|
8074
|
+
"async-close"
|
|
8075
|
+
];
|
|
8076
|
+
var json3 = (value) => ({ ok: true, kind: "json", json: value });
|
|
8077
|
+
var done2 = { ok: true, kind: "void" };
|
|
8078
|
+
var LockTable = class {
|
|
8079
|
+
held = /* @__PURE__ */ new Map();
|
|
8080
|
+
lock(pid, path, exclusive, fd) {
|
|
8081
|
+
const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
|
|
8082
|
+
for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
|
|
8083
|
+
const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
|
|
8084
|
+
mine.exclusive = exclusive;
|
|
8085
|
+
mine.fds.add(fd);
|
|
8086
|
+
holders.set(pid, mine);
|
|
8087
|
+
this.held.set(path, holders);
|
|
8088
|
+
return true;
|
|
8089
|
+
}
|
|
8090
|
+
unlock(pid, path) {
|
|
8091
|
+
const holders = this.held.get(path);
|
|
8092
|
+
holders?.delete(pid);
|
|
8093
|
+
if (holders?.size === 0) this.held.delete(path);
|
|
8094
|
+
}
|
|
8095
|
+
closed(pid, fd) {
|
|
8096
|
+
for (const [path, holders] of [...this.held]) {
|
|
8097
|
+
const mine = holders.get(pid);
|
|
8098
|
+
if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
release(pid) {
|
|
8102
|
+
for (const path of [...this.held.keys()]) this.unlock(pid, path);
|
|
8103
|
+
}
|
|
8104
|
+
};
|
|
8105
|
+
var AsyncOps = class {
|
|
8106
|
+
next = 1;
|
|
8107
|
+
results = /* @__PURE__ */ new Map();
|
|
8108
|
+
ready = [];
|
|
8109
|
+
running = /* @__PURE__ */ new Map();
|
|
8110
|
+
waiters = /* @__PURE__ */ new Set();
|
|
8111
|
+
closed = false;
|
|
8112
|
+
run;
|
|
8113
|
+
onReady;
|
|
8114
|
+
constructor(run2, onReady) {
|
|
8115
|
+
this.run = run2;
|
|
8116
|
+
this.onReady = onReady;
|
|
8117
|
+
}
|
|
8118
|
+
submit(req) {
|
|
8119
|
+
const id2 = this.hold();
|
|
8120
|
+
const running = new AbortController();
|
|
8121
|
+
this.running.set(id2, running);
|
|
8122
|
+
this.run(req, running.signal).then(
|
|
8123
|
+
(result) => this.complete(id2, result),
|
|
8124
|
+
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
8125
|
+
);
|
|
8126
|
+
return id2;
|
|
8127
|
+
}
|
|
8128
|
+
resolve(value) {
|
|
8129
|
+
const id2 = this.hold();
|
|
8130
|
+
this.complete(id2, json3(value));
|
|
8131
|
+
return id2;
|
|
8132
|
+
}
|
|
8133
|
+
hold() {
|
|
8134
|
+
const id2 = this.next++;
|
|
8135
|
+
this.results.set(id2, void 0);
|
|
8136
|
+
return id2;
|
|
8137
|
+
}
|
|
8138
|
+
take(id2) {
|
|
8139
|
+
if (!this.results.has(id2)) throw new KernelError("EINVAL");
|
|
8140
|
+
const result = this.results.get(id2);
|
|
8141
|
+
if (!result) throw new KernelError("EAGAIN");
|
|
8142
|
+
this.forget(id2);
|
|
8143
|
+
return result;
|
|
8144
|
+
}
|
|
8145
|
+
cancel(id2) {
|
|
8146
|
+
this.running.get(id2)?.abort();
|
|
8147
|
+
this.forget(id2);
|
|
8148
|
+
}
|
|
8149
|
+
forget(id2) {
|
|
8150
|
+
this.running.delete(id2);
|
|
8151
|
+
this.results.delete(id2);
|
|
8152
|
+
const at = this.ready.indexOf(id2);
|
|
8153
|
+
if (at >= 0) this.ready.splice(at, 1);
|
|
8154
|
+
}
|
|
8155
|
+
async wait(timeoutMs, blocking) {
|
|
8156
|
+
let signal;
|
|
8157
|
+
for (; ; ) {
|
|
8158
|
+
if (this.closed) return -1;
|
|
8159
|
+
const id2 = this.ready.shift();
|
|
8160
|
+
if (id2 !== void 0) return id2;
|
|
8161
|
+
if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
|
|
8162
|
+
signal ??= blocking();
|
|
8163
|
+
const woke = await this.sleep(timeoutMs, signal);
|
|
8164
|
+
if (!woke) return 0;
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
close() {
|
|
8168
|
+
this.closed = true;
|
|
8169
|
+
this.wake();
|
|
8170
|
+
}
|
|
8171
|
+
sleep(timeoutMs, signal) {
|
|
8172
|
+
return new Promise((resolve, reject) => {
|
|
8173
|
+
let timer;
|
|
8174
|
+
const cleanup = () => {
|
|
8175
|
+
clearTimeout(timer);
|
|
8176
|
+
this.waiters.delete(wake);
|
|
8177
|
+
signal.removeEventListener("abort", abort);
|
|
8178
|
+
};
|
|
8179
|
+
const wake = () => {
|
|
8180
|
+
cleanup();
|
|
8181
|
+
resolve(true);
|
|
8182
|
+
};
|
|
8183
|
+
const abort = () => {
|
|
8184
|
+
cleanup();
|
|
8185
|
+
reject(new KernelError("EINTR"));
|
|
8186
|
+
};
|
|
8187
|
+
this.waiters.add(wake);
|
|
8188
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
8189
|
+
if (timeoutMs !== void 0) {
|
|
8190
|
+
timer = setTimeout(() => {
|
|
8191
|
+
cleanup();
|
|
8192
|
+
resolve(false);
|
|
8193
|
+
}, timeoutMs);
|
|
8194
|
+
}
|
|
8195
|
+
});
|
|
8196
|
+
}
|
|
8197
|
+
complete(id2, result) {
|
|
8198
|
+
this.running.delete(id2);
|
|
8199
|
+
if (!this.results.has(id2)) return;
|
|
8200
|
+
this.results.set(id2, result);
|
|
8201
|
+
this.ready.push(id2);
|
|
8202
|
+
this.wake();
|
|
8203
|
+
this.onReady?.();
|
|
8204
|
+
}
|
|
8205
|
+
wake() {
|
|
8206
|
+
for (const wake of [...this.waiters]) wake();
|
|
8207
|
+
}
|
|
8208
|
+
};
|
|
8209
|
+
async function hostSyscall(req, ctx) {
|
|
7606
8210
|
switch (req.op) {
|
|
7607
8211
|
case "lock":
|
|
7608
8212
|
if (!ctx.locks) throw new KernelError("ENOSYS");
|
|
@@ -7616,7 +8220,7 @@ async function hostSyscall(req, ctx) {
|
|
|
7616
8220
|
if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
|
|
7617
8221
|
return json3(ctx.ops.submit(req.req));
|
|
7618
8222
|
case "async-wait":
|
|
7619
|
-
return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking
|
|
8223
|
+
return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
|
|
7620
8224
|
case "async-take":
|
|
7621
8225
|
return ctx.ops.take(req.id);
|
|
7622
8226
|
case "async-resolve":
|
|
@@ -8504,343 +9108,118 @@ async function selectFds(fds, read, write, timeoutMs, interrupt) {
|
|
|
8504
9108
|
try {
|
|
8505
9109
|
return await waitReady(fds, read, write, deadline, interrupted2.promise);
|
|
8506
9110
|
} finally {
|
|
8507
|
-
interrupted2.done();
|
|
8508
|
-
}
|
|
8509
|
-
}
|
|
8510
|
-
async function waitReady(fds, read, write, deadline, interrupted2) {
|
|
8511
|
-
for (; ; ) {
|
|
8512
|
-
const now = ready(fds, read, write);
|
|
8513
|
-
if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
|
|
8514
|
-
const round = new AbortController();
|
|
8515
|
-
const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
|
|
8516
|
-
const file = fds.get(fd).file;
|
|
8517
|
-
return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
|
|
8518
|
-
});
|
|
8519
|
-
let timer;
|
|
8520
|
-
const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
|
|
8521
|
-
try {
|
|
8522
|
-
await Promise.race([...changes, ...expiry, interrupted2]);
|
|
8523
|
-
} finally {
|
|
8524
|
-
round.abort();
|
|
8525
|
-
clearTimeout(timer);
|
|
8526
|
-
}
|
|
8527
|
-
}
|
|
8528
|
-
}
|
|
8529
|
-
|
|
8530
|
-
// src/kernel/socket-syscalls.ts
|
|
8531
|
-
var SOCKET_OPS = [
|
|
8532
|
-
"sock-open",
|
|
8533
|
-
"sock-pair",
|
|
8534
|
-
"sock-bind",
|
|
8535
|
-
"sock-listen",
|
|
8536
|
-
"sock-accept",
|
|
8537
|
-
"sock-connect",
|
|
8538
|
-
"sock-shutdown",
|
|
8539
|
-
"sock-name",
|
|
8540
|
-
"sock-getopt",
|
|
8541
|
-
"sock-setopt",
|
|
8542
|
-
"sock-resolve"
|
|
8543
|
-
];
|
|
8544
|
-
function socketAt(fds, fd) {
|
|
8545
|
-
const file = fds.get(fd).file;
|
|
8546
|
-
if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
|
|
8547
|
-
return file;
|
|
8548
|
-
}
|
|
8549
|
-
var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
8550
|
-
async function socketSyscall(req, proc) {
|
|
8551
|
-
const { fds, net } = proc;
|
|
8552
|
-
switch (req.op) {
|
|
8553
|
-
case "sock-open":
|
|
8554
|
-
return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
|
|
8555
|
-
case "sock-pair": {
|
|
8556
|
-
const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
|
|
8557
|
-
const first = fds.install(new OpenFile(a), 3);
|
|
8558
|
-
try {
|
|
8559
|
-
return ok([first, fds.install(new OpenFile(b), 3)]);
|
|
8560
|
-
} catch (e) {
|
|
8561
|
-
await Promise.resolve(fds.close(first));
|
|
8562
|
-
throw e;
|
|
8563
|
-
}
|
|
8564
|
-
}
|
|
8565
|
-
case "sock-bind":
|
|
8566
|
-
socketAt(fds, req.fd).bind(req.addr);
|
|
8567
|
-
return ok();
|
|
8568
|
-
case "sock-listen":
|
|
8569
|
-
socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
|
|
8570
|
-
return ok();
|
|
8571
|
-
case "sock-accept":
|
|
8572
|
-
return ok(await accept(proc, req.fd, req.nonblock));
|
|
8573
|
-
case "sock-connect": {
|
|
8574
|
-
const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
|
|
8575
|
-
if (req.nonblock) {
|
|
8576
|
-
dialing?.catch(() => void 0);
|
|
8577
|
-
throw new KernelError("EINPROGRESS");
|
|
8578
|
-
}
|
|
8579
|
-
if (dialing !== void 0) await interruptible(dialing, proc.blocking());
|
|
8580
|
-
return ok();
|
|
8581
|
-
}
|
|
8582
|
-
case "sock-shutdown":
|
|
8583
|
-
socketAt(fds, req.fd).shutdown(req.how);
|
|
8584
|
-
return ok();
|
|
8585
|
-
case "sock-name": {
|
|
8586
|
-
const socket = socketAt(fds, req.fd);
|
|
8587
|
-
const addr = req.peer ? socket.peer : socket.local;
|
|
8588
|
-
if (req.peer && !addr) throw new KernelError("ENOTCONN");
|
|
8589
|
-
return ok(addr ?? unnamed(socket.domain));
|
|
8590
|
-
}
|
|
8591
|
-
case "sock-getopt":
|
|
8592
|
-
return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
|
|
8593
|
-
case "sock-setopt":
|
|
8594
|
-
socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
|
|
8595
|
-
return ok();
|
|
8596
|
-
case "sock-resolve": {
|
|
8597
|
-
const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
|
|
8598
|
-
if (addresses.length === 0) throw new KernelError("ENOENT");
|
|
8599
|
-
return ok(addresses);
|
|
8600
|
-
}
|
|
8601
|
-
}
|
|
8602
|
-
}
|
|
8603
|
-
function interruptible(dialing, signal) {
|
|
8604
|
-
return new Promise((resolve, reject) => {
|
|
8605
|
-
const abort = () => reject(new KernelError("EINTR"));
|
|
8606
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
8607
|
-
dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
8608
|
-
});
|
|
8609
|
-
}
|
|
8610
|
-
function unnamed(domain) {
|
|
8611
|
-
return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
|
|
8612
|
-
}
|
|
8613
|
-
async function accept(proc, fd, nonblock) {
|
|
8614
|
-
const listener = socketAt(proc.fds, fd);
|
|
8615
|
-
const waits = !nonblock && !pollFile(listener).readable;
|
|
8616
|
-
const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
|
|
8617
|
-
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
8618
|
-
}
|
|
8619
|
-
|
|
8620
|
-
// src/fs/pages.ts
|
|
8621
|
-
var PAGE = 64 * 1024;
|
|
8622
|
-
var READAHEAD = 1024 * 1024;
|
|
8623
|
-
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
8624
|
-
var WRITEBACK = 8 * 1024 * 1024;
|
|
8625
|
-
var RUN = 1024 * 1024;
|
|
8626
|
-
var Pages = class {
|
|
8627
|
-
length;
|
|
8628
|
-
stored;
|
|
8629
|
-
shrink;
|
|
8630
|
-
pages = /* @__PURE__ */ new Map();
|
|
8631
|
-
dirty = /* @__PURE__ */ new Set();
|
|
8632
|
-
free = [];
|
|
8633
|
-
pinned = false;
|
|
8634
|
-
constructor(size) {
|
|
8635
|
-
this.length = size;
|
|
8636
|
-
this.stored = size;
|
|
8637
|
-
}
|
|
8638
|
-
get dirtyBytes() {
|
|
8639
|
-
return this.dirty.size * PAGE;
|
|
8640
|
-
}
|
|
8641
|
-
get changed() {
|
|
8642
|
-
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
8643
|
-
}
|
|
8644
|
-
missing(at, length2) {
|
|
8645
|
-
this.evict();
|
|
8646
|
-
const end = Math.min(this.stored, at + length2, this.length);
|
|
8647
|
-
const runs = [];
|
|
8648
|
-
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
8649
|
-
if (this.pages.has(page)) continue;
|
|
8650
|
-
const last2 = runs[runs.length - 1];
|
|
8651
|
-
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
8652
|
-
else runs.push([page * PAGE, PAGE]);
|
|
8653
|
-
}
|
|
8654
|
-
const last = runs[runs.length - 1];
|
|
8655
|
-
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
8656
|
-
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
8657
|
-
}
|
|
8658
|
-
partial(at, length2) {
|
|
8659
|
-
if (length2 === 0) return [];
|
|
8660
|
-
this.evict();
|
|
8661
|
-
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
8662
|
-
const runs = [];
|
|
8663
|
-
for (const page of edges) {
|
|
8664
|
-
const start2 = page * PAGE;
|
|
8665
|
-
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
8666
|
-
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
8667
|
-
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
8668
|
-
}
|
|
8669
|
-
return runs;
|
|
8670
|
-
}
|
|
8671
|
-
fill(start2, bytes) {
|
|
8672
|
-
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
8673
|
-
const page = (start2 + at) / PAGE;
|
|
8674
|
-
if (this.pages.has(page)) continue;
|
|
8675
|
-
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
8676
|
-
const fresh = this.take(part.length);
|
|
8677
|
-
fresh.set(part);
|
|
8678
|
-
this.pages.set(page, fresh);
|
|
8679
|
-
}
|
|
8680
|
-
}
|
|
8681
|
-
read(at, length2) {
|
|
8682
|
-
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
8683
|
-
this.readInto(at, out);
|
|
8684
|
-
return out;
|
|
8685
|
-
}
|
|
8686
|
-
readInto(at, out) {
|
|
8687
|
-
const end = Math.min(this.length, at + out.length);
|
|
8688
|
-
for (let pos = at; pos < end; ) {
|
|
8689
|
-
const page = Math.floor(pos / PAGE);
|
|
8690
|
-
const from = pos - page * PAGE;
|
|
8691
|
-
const n = Math.min(PAGE - from, end - pos);
|
|
8692
|
-
const bytes = this.pages.get(page);
|
|
8693
|
-
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
8694
|
-
else out.fill(0, pos - at, pos - at + n);
|
|
8695
|
-
pos += n;
|
|
8696
|
-
}
|
|
8697
|
-
return Math.max(0, end - at);
|
|
8698
|
-
}
|
|
8699
|
-
write(at, bytes) {
|
|
8700
|
-
for (let done4 = 0; done4 < bytes.length; ) {
|
|
8701
|
-
const pos = at + done4;
|
|
8702
|
-
const page = Math.floor(pos / PAGE);
|
|
8703
|
-
const from = pos - page * PAGE;
|
|
8704
|
-
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
8705
|
-
let target = this.pages.get(page);
|
|
8706
|
-
if (!target) {
|
|
8707
|
-
target = this.take(0);
|
|
8708
|
-
this.pages.set(page, target);
|
|
8709
|
-
}
|
|
8710
|
-
target.set(bytes.subarray(done4, done4 + n), from);
|
|
8711
|
-
this.dirty.add(page);
|
|
8712
|
-
done4 += n;
|
|
8713
|
-
}
|
|
8714
|
-
this.length = Math.max(this.length, at + bytes.length);
|
|
9111
|
+
interrupted2.done();
|
|
8715
9112
|
}
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
9113
|
+
}
|
|
9114
|
+
async function waitReady(fds, read, write, deadline, interrupted2) {
|
|
9115
|
+
for (; ; ) {
|
|
9116
|
+
const now = ready(fds, read, write);
|
|
9117
|
+
if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
|
|
9118
|
+
const round = new AbortController();
|
|
9119
|
+
const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
|
|
9120
|
+
const file = fds.get(fd).file;
|
|
9121
|
+
return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
|
|
9122
|
+
});
|
|
9123
|
+
let timer;
|
|
9124
|
+
const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
|
|
9125
|
+
try {
|
|
9126
|
+
await Promise.race([...changes, ...expiry, interrupted2]);
|
|
9127
|
+
} finally {
|
|
9128
|
+
round.abort();
|
|
9129
|
+
clearTimeout(timer);
|
|
8727
9130
|
}
|
|
8728
|
-
this.length = size;
|
|
8729
9131
|
}
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
9132
|
+
}
|
|
9133
|
+
|
|
9134
|
+
// src/kernel/socket-syscalls.ts
|
|
9135
|
+
var SOCKET_OPS = [
|
|
9136
|
+
"sock-open",
|
|
9137
|
+
"sock-pair",
|
|
9138
|
+
"sock-bind",
|
|
9139
|
+
"sock-listen",
|
|
9140
|
+
"sock-accept",
|
|
9141
|
+
"sock-connect",
|
|
9142
|
+
"sock-shutdown",
|
|
9143
|
+
"sock-name",
|
|
9144
|
+
"sock-getopt",
|
|
9145
|
+
"sock-setopt",
|
|
9146
|
+
"sock-resolve"
|
|
9147
|
+
];
|
|
9148
|
+
function socketAt(fds, fd) {
|
|
9149
|
+
const file = fds.get(fd).file;
|
|
9150
|
+
if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
|
|
9151
|
+
return file;
|
|
9152
|
+
}
|
|
9153
|
+
var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
|
|
9154
|
+
async function socketSyscall(req, proc) {
|
|
9155
|
+
const { fds, net } = proc;
|
|
9156
|
+
switch (req.op) {
|
|
9157
|
+
case "sock-open":
|
|
9158
|
+
return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
|
|
9159
|
+
case "sock-pair": {
|
|
9160
|
+
const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
|
|
9161
|
+
const first = fds.install(new OpenFile(a), 3);
|
|
9162
|
+
try {
|
|
9163
|
+
return ok([first, fds.install(new OpenFile(b), 3)]);
|
|
9164
|
+
} catch (e) {
|
|
9165
|
+
await Promise.resolve(fds.close(first));
|
|
9166
|
+
throw e;
|
|
8738
9167
|
}
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
9168
|
+
}
|
|
9169
|
+
case "sock-bind":
|
|
9170
|
+
socketAt(fds, req.fd).bind(req.addr);
|
|
9171
|
+
return ok();
|
|
9172
|
+
case "sock-listen":
|
|
9173
|
+
socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
|
|
9174
|
+
return ok();
|
|
9175
|
+
case "sock-accept":
|
|
9176
|
+
return ok(await accept(proc, req.fd, req.nonblock));
|
|
9177
|
+
case "sock-connect": {
|
|
9178
|
+
const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
|
|
9179
|
+
if (req.nonblock) {
|
|
9180
|
+
dialing?.catch(() => void 0);
|
|
9181
|
+
throw new KernelError("EINPROGRESS");
|
|
8745
9182
|
}
|
|
8746
|
-
|
|
8747
|
-
|
|
9183
|
+
if (dialing !== void 0) await interruptible(dialing, proc.blocking());
|
|
9184
|
+
return ok();
|
|
8748
9185
|
}
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
this.dirty.clear();
|
|
8758
|
-
this.shrink = void 0;
|
|
8759
|
-
this.stored = this.length;
|
|
8760
|
-
this.evict();
|
|
8761
|
-
}
|
|
8762
|
-
pin() {
|
|
8763
|
-
this.pinned = true;
|
|
8764
|
-
}
|
|
8765
|
-
reset(size) {
|
|
8766
|
-
if (this.pinned) return;
|
|
8767
|
-
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
8768
|
-
if (this.changed) return;
|
|
8769
|
-
this.length = size;
|
|
8770
|
-
this.stored = size;
|
|
8771
|
-
}
|
|
8772
|
-
evict() {
|
|
8773
|
-
if (this.pinned) return;
|
|
8774
|
-
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
8775
|
-
for (const page of this.pages.keys()) {
|
|
8776
|
-
if (clean <= CLEAN_LIMIT) return;
|
|
8777
|
-
if (this.dirty.has(page)) continue;
|
|
8778
|
-
this.drop(page);
|
|
8779
|
-
clean -= PAGE;
|
|
9186
|
+
case "sock-shutdown":
|
|
9187
|
+
socketAt(fds, req.fd).shutdown(req.how);
|
|
9188
|
+
return ok();
|
|
9189
|
+
case "sock-name": {
|
|
9190
|
+
const socket = socketAt(fds, req.fd);
|
|
9191
|
+
const addr = req.peer ? socket.peer : socket.local;
|
|
9192
|
+
if (req.peer && !addr) throw new KernelError("ENOTCONN");
|
|
9193
|
+
return ok(addr ?? unnamed(socket.domain));
|
|
8780
9194
|
}
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
this.pages.delete(page);
|
|
8791
|
-
this.dirty.delete(page);
|
|
8792
|
-
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
8793
|
-
}
|
|
8794
|
-
};
|
|
8795
|
-
|
|
8796
|
-
// src/fs/ranged.ts
|
|
8797
|
-
var AsyncRangedFile = class {
|
|
8798
|
-
pages;
|
|
8799
|
-
io;
|
|
8800
|
-
pinned = false;
|
|
8801
|
-
version;
|
|
8802
|
-
path;
|
|
8803
|
-
constructor(io, path, size, version) {
|
|
8804
|
-
this.io = io;
|
|
8805
|
-
this.path = path;
|
|
8806
|
-
this.version = version;
|
|
8807
|
-
this.pages = new Pages(size);
|
|
8808
|
-
}
|
|
8809
|
-
size() {
|
|
8810
|
-
return this.pages.length;
|
|
8811
|
-
}
|
|
8812
|
-
async fetch(runs) {
|
|
8813
|
-
for (const [start2, n] of runs) {
|
|
8814
|
-
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
9195
|
+
case "sock-getopt":
|
|
9196
|
+
return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
|
|
9197
|
+
case "sock-setopt":
|
|
9198
|
+
socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
|
|
9199
|
+
return ok();
|
|
9200
|
+
case "sock-resolve": {
|
|
9201
|
+
const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
|
|
9202
|
+
if (addresses.length === 0) throw new KernelError("ENOENT");
|
|
9203
|
+
return ok(addresses);
|
|
8815
9204
|
}
|
|
8816
9205
|
}
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
8835
|
-
this.version = void 0;
|
|
8836
|
-
this.pages.flushed();
|
|
8837
|
-
}
|
|
8838
|
-
async pin() {
|
|
8839
|
-
this.pinned = true;
|
|
8840
|
-
this.pages.pin();
|
|
8841
|
-
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
8842
|
-
}
|
|
8843
|
-
};
|
|
9206
|
+
}
|
|
9207
|
+
function interruptible(dialing, signal) {
|
|
9208
|
+
return new Promise((resolve, reject) => {
|
|
9209
|
+
const abort = () => reject(new KernelError("EINTR"));
|
|
9210
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
9211
|
+
dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
9212
|
+
});
|
|
9213
|
+
}
|
|
9214
|
+
function unnamed(domain) {
|
|
9215
|
+
return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
|
|
9216
|
+
}
|
|
9217
|
+
async function accept(proc, fd, nonblock) {
|
|
9218
|
+
const listener = socketAt(proc.fds, fd);
|
|
9219
|
+
const waits = !nonblock && !pollFile(listener).readable;
|
|
9220
|
+
const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
|
|
9221
|
+
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
9222
|
+
}
|
|
8844
9223
|
|
|
8845
9224
|
// src/kernel/vfs-file.ts
|
|
8846
9225
|
var O_ACCMODE = 3;
|
|
@@ -9443,6 +9822,9 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
9443
9822
|
"dl-closure",
|
|
9444
9823
|
"dl-closure-free",
|
|
9445
9824
|
"dl-reserve",
|
|
9825
|
+
"dl-load-reserve",
|
|
9826
|
+
"dl-lock",
|
|
9827
|
+
"dl-unlock",
|
|
9446
9828
|
"fd-renumber",
|
|
9447
9829
|
"fd-promote",
|
|
9448
9830
|
"fd-open-tty",
|
|
@@ -9481,6 +9863,7 @@ function isWasmSyscall(req) {
|
|
|
9481
9863
|
return typeof op === "string" && SYSCALL_OPS.has(op);
|
|
9482
9864
|
}
|
|
9483
9865
|
var MAX_READ = 1024 * 1024;
|
|
9866
|
+
var WRITE_ACCESS = 3;
|
|
9484
9867
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
9485
9868
|
var DEFAULT_UMASK = 18;
|
|
9486
9869
|
var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
|
|
@@ -9501,7 +9884,7 @@ var WasmProcess = class {
|
|
|
9501
9884
|
stopped = 0;
|
|
9502
9885
|
stops = 0;
|
|
9503
9886
|
owed = false;
|
|
9504
|
-
|
|
9887
|
+
waiting = /* @__PURE__ */ new Set();
|
|
9505
9888
|
resumed = Promise.resolve();
|
|
9506
9889
|
wake;
|
|
9507
9890
|
stateListeners = [];
|
|
@@ -9557,7 +9940,8 @@ var WasmProcess = class {
|
|
|
9557
9940
|
return "terminate";
|
|
9558
9941
|
}
|
|
9559
9942
|
this.options.onPending?.(sig);
|
|
9560
|
-
if (this.options.jobs?.keyboard && KEYBOARD.has(sig)
|
|
9943
|
+
if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
|
|
9944
|
+
this.owed = true;
|
|
9561
9945
|
const blocked = this.interrupt;
|
|
9562
9946
|
this.interrupt = new AbortController();
|
|
9563
9947
|
blocked.abort();
|
|
@@ -9602,10 +9986,15 @@ var WasmProcess = class {
|
|
|
9602
9986
|
await this.resumed;
|
|
9603
9987
|
if (this.dying) return DYING;
|
|
9604
9988
|
const stops = this.stops;
|
|
9605
|
-
const
|
|
9606
|
-
const
|
|
9989
|
+
const scope = new AbortController();
|
|
9990
|
+
const forward = () => scope.abort();
|
|
9991
|
+
if (cancelled?.aborted) scope.abort();
|
|
9992
|
+
cancelled?.addEventListener("abort", forward, { once: true });
|
|
9993
|
+
const result = await this.dispatch(req, scope.signal).finally(() => {
|
|
9994
|
+
cancelled?.removeEventListener("abort", forward);
|
|
9995
|
+
this.waiting.delete(scope.signal);
|
|
9996
|
+
});
|
|
9607
9997
|
if (this.dying) return DYING;
|
|
9608
|
-
if (!result.ok && result.errno === "EINTR" && this.waits !== waits) this.owed = false;
|
|
9609
9998
|
const restart = !result.ok && result.errno === "EINTR" && this.stops !== stops;
|
|
9610
9999
|
if (restart && !this.options.hasPending?.()) continue;
|
|
9611
10000
|
await this.resumed;
|
|
@@ -9658,12 +10047,31 @@ var WasmProcess = class {
|
|
|
9658
10047
|
refuseIfPending(restart) {
|
|
9659
10048
|
if (restart && this.options.hasPending?.()) throw new KernelError("EINTR");
|
|
9660
10049
|
}
|
|
10050
|
+
async procWait(req, cancelled) {
|
|
10051
|
+
const flags = {
|
|
10052
|
+
untraced: req.untraced,
|
|
10053
|
+
continued: req.continued,
|
|
10054
|
+
inGroup: this.waitGroup(req.pid)
|
|
10055
|
+
};
|
|
10056
|
+
if (this.owed && !req.nohang) {
|
|
10057
|
+
const now = await this.children.wait(req.pid, true, this.interrupt.signal, flags);
|
|
10058
|
+
if (now[0] !== 0) return now;
|
|
10059
|
+
}
|
|
10060
|
+
const signal = req.nohang ? this.interrupt.signal : this.blockingSignal(cancelled);
|
|
10061
|
+
const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
|
|
10062
|
+
return waited;
|
|
10063
|
+
}
|
|
9661
10064
|
blockingSignal(cancelled, owing = true) {
|
|
9662
|
-
if (owing
|
|
9663
|
-
|
|
10065
|
+
if (owing && this.owed) {
|
|
10066
|
+
this.owed = false;
|
|
9664
10067
|
throw new KernelError("EINTR");
|
|
9665
10068
|
}
|
|
9666
|
-
|
|
10069
|
+
if (this.options.hasPending?.() || cancelled.aborted) throw new KernelError("EINTR");
|
|
10070
|
+
if (owing) this.waiting.add(cancelled);
|
|
10071
|
+
return this.cancelSignal(cancelled);
|
|
10072
|
+
}
|
|
10073
|
+
cancelSignal(cancelled) {
|
|
10074
|
+
return AbortSignal.any([this.interrupt.signal, cancelled]);
|
|
9667
10075
|
}
|
|
9668
10076
|
async read(req, cancelled) {
|
|
9669
10077
|
const file = this.fds.get(req.fd).file;
|
|
@@ -9687,15 +10095,22 @@ var WasmProcess = class {
|
|
|
9687
10095
|
if (nonblock) return file.write(body, AbortSignal.abort());
|
|
9688
10096
|
return file.write(
|
|
9689
10097
|
body,
|
|
9690
|
-
this.options.hasPending?.() ? AbortSignal.abort() : this.
|
|
10098
|
+
this.options.hasPending?.() ? AbortSignal.abort() : this.cancelSignal(cancelled)
|
|
9691
10099
|
);
|
|
9692
10100
|
}
|
|
9693
10101
|
async openVfs(req) {
|
|
10102
|
+
if (inProc(req.path)) return this.openProc(req, req.path);
|
|
10103
|
+
const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
|
|
10104
|
+
if (alias !== void 0) return this.openProc(req, alias);
|
|
9694
10105
|
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
9695
10106
|
const fd = this.fds.install(heldFile(), 3);
|
|
9696
10107
|
try {
|
|
9697
10108
|
const nodes = this.nodes;
|
|
9698
10109
|
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : req.create ? await nodes.targetKey(req.path) : await this.keptOr(await nodes.targetKey(req.path), req.handle);
|
|
10110
|
+
if (inProc(path)) {
|
|
10111
|
+
this.fds.installAt(fd, await this.procFile(req, path));
|
|
10112
|
+
return fd;
|
|
10113
|
+
}
|
|
9699
10114
|
const open = { ...req, path };
|
|
9700
10115
|
if (req.exclusive) {
|
|
9701
10116
|
await nodes.createExclusive(path);
|
|
@@ -9717,6 +10132,22 @@ var WasmProcess = class {
|
|
|
9717
10132
|
}
|
|
9718
10133
|
return fd;
|
|
9719
10134
|
}
|
|
10135
|
+
async openProc(req, path) {
|
|
10136
|
+
return this.fds.install(await this.procFile(req, path), 3);
|
|
10137
|
+
}
|
|
10138
|
+
async procFile(req, path) {
|
|
10139
|
+
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10140
|
+
throw new KernelError("EROFS");
|
|
10141
|
+
}
|
|
10142
|
+
const tree = new ProcTree(
|
|
10143
|
+
{
|
|
10144
|
+
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10145
|
+
mountList: () => this.options.mounts?.() ?? []
|
|
10146
|
+
},
|
|
10147
|
+
(await this.options.identity?.())?.pid ?? this.pid
|
|
10148
|
+
);
|
|
10149
|
+
return memoryFile(tree.read(path), path, req.position);
|
|
10150
|
+
}
|
|
9720
10151
|
openVfsFile(req) {
|
|
9721
10152
|
return vfsFile(
|
|
9722
10153
|
this.options.fs,
|
|
@@ -9808,6 +10239,11 @@ var WasmProcess = class {
|
|
|
9808
10239
|
return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
|
|
9809
10240
|
case "fd-select": {
|
|
9810
10241
|
const { read, write, timeoutMs } = req;
|
|
10242
|
+
if (this.owed && timeoutMs !== 0) {
|
|
10243
|
+
const ready2 = await selectFds(this.fds, read, write, 0, this.interrupt.signal);
|
|
10244
|
+
if (ready2.read.length + ready2.write.length > 0)
|
|
10245
|
+
return { ok: true, kind: "json", json: ready2 };
|
|
10246
|
+
}
|
|
9811
10247
|
const signal = this.blockingSignal(cancelled, timeoutMs !== 0);
|
|
9812
10248
|
const selected = await selectFds(this.fds, read, write, timeoutMs, signal);
|
|
9813
10249
|
return { ok: true, kind: "json", json: selected };
|
|
@@ -10005,16 +10441,8 @@ var WasmProcess = class {
|
|
|
10005
10441
|
case "proc-spawn":
|
|
10006
10442
|
this.refuseIfPending(req.restart);
|
|
10007
10443
|
return { ok: true, kind: "json", json: await this.spawn(req) };
|
|
10008
|
-
case "proc-wait":
|
|
10009
|
-
|
|
10010
|
-
const flags = {
|
|
10011
|
-
untraced: req.untraced,
|
|
10012
|
-
continued: req.continued,
|
|
10013
|
-
inGroup: this.waitGroup(req.pid)
|
|
10014
|
-
};
|
|
10015
|
-
const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
|
|
10016
|
-
return { ok: true, kind: "json", json: waited };
|
|
10017
|
-
}
|
|
10444
|
+
case "proc-wait":
|
|
10445
|
+
return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
|
|
10018
10446
|
case "proc-identity":
|
|
10019
10447
|
return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
|
|
10020
10448
|
case "proc-exec": {
|
|
@@ -10046,7 +10474,7 @@ var WasmProcess = class {
|
|
|
10046
10474
|
return { ok: true, kind: "json", json: this.options.mounts?.() ?? [] };
|
|
10047
10475
|
case "mount":
|
|
10048
10476
|
case "umount":
|
|
10049
|
-
return this.mountSyscall(req);
|
|
10477
|
+
return this.mountSyscall(req, cancelled);
|
|
10050
10478
|
case "proc-alarm":
|
|
10051
10479
|
this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
|
|
10052
10480
|
return { ok: true, kind: "void" };
|
|
@@ -10067,6 +10495,18 @@ var WasmProcess = class {
|
|
|
10067
10495
|
return { ok: true, kind: "void" };
|
|
10068
10496
|
case "dl-reserve":
|
|
10069
10497
|
return { ok: true, kind: "json", json: this.reserveSlots(req.floor, req.size) };
|
|
10498
|
+
case "dl-load-reserve":
|
|
10499
|
+
return {
|
|
10500
|
+
ok: true,
|
|
10501
|
+
kind: "json",
|
|
10502
|
+
json: { base: this.reserveSlots(req.floor, req.size), handle: this.closures.handle++ }
|
|
10503
|
+
};
|
|
10504
|
+
case "dl-lock":
|
|
10505
|
+
await this.lockDl(req.owner);
|
|
10506
|
+
return { ok: true, kind: "void" };
|
|
10507
|
+
case "dl-unlock":
|
|
10508
|
+
this.unlockDl(req.owner);
|
|
10509
|
+
return { ok: true, kind: "void" };
|
|
10070
10510
|
case "sig-mask":
|
|
10071
10511
|
this.caught = req.caught;
|
|
10072
10512
|
this.ignored = req.ignored;
|
|
@@ -10081,14 +10521,14 @@ var WasmProcess = class {
|
|
|
10081
10521
|
return { ok: true, kind: "bytes", bytes: this.children.captured(req.pid, req.slot) };
|
|
10082
10522
|
}
|
|
10083
10523
|
}
|
|
10084
|
-
async mountSyscall(req) {
|
|
10524
|
+
async mountSyscall(req, cancelled) {
|
|
10085
10525
|
const { mount, umount } = this.options;
|
|
10086
10526
|
if (!mount || !umount) throw new KernelError("ENOSYS");
|
|
10087
10527
|
try {
|
|
10088
10528
|
if (req.op === "umount") await umount(req.target, req.flags);
|
|
10089
10529
|
else {
|
|
10090
10530
|
const { source, target, type, flags, data } = req;
|
|
10091
|
-
const signal = this.blockingSignal();
|
|
10531
|
+
const signal = this.blockingSignal(cancelled);
|
|
10092
10532
|
await Promise.race([mount({ source, target, type, flags, data }, signal), aborted(signal)]);
|
|
10093
10533
|
}
|
|
10094
10534
|
} catch (err) {
|
|
@@ -10103,7 +10543,12 @@ var WasmProcess = class {
|
|
|
10103
10543
|
}
|
|
10104
10544
|
alarm;
|
|
10105
10545
|
dlLog = [];
|
|
10106
|
-
closures = {
|
|
10546
|
+
closures = {
|
|
10547
|
+
next: 0,
|
|
10548
|
+
handle: 1,
|
|
10549
|
+
free: [],
|
|
10550
|
+
live: /* @__PURE__ */ new Set()
|
|
10551
|
+
};
|
|
10107
10552
|
allocateClosure(floor) {
|
|
10108
10553
|
const index = this.closures.free.pop() ?? Math.max(floor, this.closures.next);
|
|
10109
10554
|
this.closures.next = Math.max(this.closures.next, index + 1);
|
|
@@ -10116,6 +10561,47 @@ var WasmProcess = class {
|
|
|
10116
10561
|
this.closures.free.push(index);
|
|
10117
10562
|
this.dlLog.push({ kind: "closure-free", index });
|
|
10118
10563
|
}
|
|
10564
|
+
dlLock = {
|
|
10565
|
+
owner: 0,
|
|
10566
|
+
depth: 0,
|
|
10567
|
+
waiters: /* @__PURE__ */ new Set()
|
|
10568
|
+
};
|
|
10569
|
+
async lockDl(owner) {
|
|
10570
|
+
while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
|
|
10571
|
+
if (this.dying) throw new KernelError("EINTR");
|
|
10572
|
+
const interrupt = this.interrupt.signal;
|
|
10573
|
+
const gone = await new Promise((resolve) => {
|
|
10574
|
+
const waiter = {
|
|
10575
|
+
owner,
|
|
10576
|
+
wake: (ended) => {
|
|
10577
|
+
this.dlLock.waiters.delete(waiter);
|
|
10578
|
+
interrupt.removeEventListener("abort", recheck);
|
|
10579
|
+
resolve(ended);
|
|
10580
|
+
}
|
|
10581
|
+
};
|
|
10582
|
+
const recheck = () => waiter.wake(false);
|
|
10583
|
+
this.dlLock.waiters.add(waiter);
|
|
10584
|
+
interrupt.addEventListener("abort", recheck, { once: true });
|
|
10585
|
+
});
|
|
10586
|
+
if (gone) throw new KernelError("EINTR");
|
|
10587
|
+
}
|
|
10588
|
+
this.dlLock.owner = owner;
|
|
10589
|
+
this.dlLock.depth++;
|
|
10590
|
+
}
|
|
10591
|
+
threadEnded(tid) {
|
|
10592
|
+
for (const waiter of [...this.dlLock.waiters]) if (waiter.owner === tid) waiter.wake(true);
|
|
10593
|
+
if (this.dlLock.depth === 0 || this.dlLock.owner !== tid) return;
|
|
10594
|
+
this.dlLock.depth = 0;
|
|
10595
|
+
this.wakeDl();
|
|
10596
|
+
}
|
|
10597
|
+
unlockDl(owner) {
|
|
10598
|
+
if (this.dlLock.depth === 0 || this.dlLock.owner !== owner) throw new KernelError("EPERM");
|
|
10599
|
+
this.dlLock.depth--;
|
|
10600
|
+
if (this.dlLock.depth === 0) this.wakeDl();
|
|
10601
|
+
}
|
|
10602
|
+
wakeDl() {
|
|
10603
|
+
for (const waiter of [...this.dlLock.waiters]) waiter.wake(false);
|
|
10604
|
+
}
|
|
10119
10605
|
reserveSlots(floor, size) {
|
|
10120
10606
|
const base = Math.max(floor, this.closures.next);
|
|
10121
10607
|
this.closures.next = base + size;
|
|
@@ -10427,6 +10913,7 @@ function spawnWasmProcess(opts) {
|
|
|
10427
10913
|
const endThread = (tid) => {
|
|
10428
10914
|
const end = threads.get(tid);
|
|
10429
10915
|
threads.delete(tid);
|
|
10916
|
+
process.threadEnded(tid);
|
|
10430
10917
|
end?.();
|
|
10431
10918
|
};
|
|
10432
10919
|
const spawnThread = (thread) => {
|
|
@@ -11219,8 +11706,8 @@ function umountCall(target, flags) {
|
|
|
11219
11706
|
var FSTAB_PATH = "/etc/fstab";
|
|
11220
11707
|
var FSTAB_RETRIES = [1e3, 4e3, 16e3];
|
|
11221
11708
|
var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
|
|
11222
|
-
function unescape(
|
|
11223
|
-
return
|
|
11709
|
+
function unescape(field3) {
|
|
11710
|
+
return field3.replace(
|
|
11224
11711
|
/\\([0-7]{3})/g,
|
|
11225
11712
|
(_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
11226
11713
|
);
|
|
@@ -12040,7 +12527,7 @@ function importedMemory(bytes) {
|
|
|
12040
12527
|
const count = u32();
|
|
12041
12528
|
for (let i = 0; i < count; i++) {
|
|
12042
12529
|
const module = name2();
|
|
12043
|
-
const
|
|
12530
|
+
const field3 = name2();
|
|
12044
12531
|
const kind = bytes[at++];
|
|
12045
12532
|
if (kind === 0) u32();
|
|
12046
12533
|
else if (kind === 1) {
|
|
@@ -12048,7 +12535,7 @@ function importedMemory(bytes) {
|
|
|
12048
12535
|
limits();
|
|
12049
12536
|
} else if (kind === 2) {
|
|
12050
12537
|
const l = limits();
|
|
12051
|
-
return { module, name:
|
|
12538
|
+
return { module, name: field3, ...l };
|
|
12052
12539
|
} else if (kind === 3) at += 2;
|
|
12053
12540
|
else if (kind === 4) {
|
|
12054
12541
|
at++;
|
|
@@ -12139,12 +12626,12 @@ function readImports(r, types) {
|
|
|
12139
12626
|
const foreign = {};
|
|
12140
12627
|
for (let i = r.u32(); i > 0; i--) {
|
|
12141
12628
|
const module = r.name();
|
|
12142
|
-
const
|
|
12629
|
+
const field3 = r.name();
|
|
12143
12630
|
const kind = r.byte();
|
|
12144
12631
|
if (kind !== 0) skipImport(r, kind);
|
|
12145
12632
|
else {
|
|
12146
12633
|
const type = types[r.u32()] ?? "other";
|
|
12147
|
-
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[
|
|
12634
|
+
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field3] = type;
|
|
12148
12635
|
}
|
|
12149
12636
|
}
|
|
12150
12637
|
return foreign;
|