@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/dist/node.js CHANGED
@@ -329,6 +329,32 @@ function bytesSource(data) {
329
329
  }
330
330
  });
331
331
  }
332
+ function memoryFile(data, path, position = 0) {
333
+ let offset = position;
334
+ return new OpenFile({
335
+ read: async (max) => {
336
+ const out = data.subarray(offset, offset + max);
337
+ offset += out.length;
338
+ return out;
339
+ },
340
+ pread: async (max, at) => data.subarray(at, at + max),
341
+ seek: async (by, whence) => {
342
+ const at = (whence === 1 ? offset : whence === 2 ? data.length : 0) + by;
343
+ if (at < 0) throw new KernelError("EINVAL");
344
+ offset = at;
345
+ return at;
346
+ },
347
+ pwrite: async () => {
348
+ throw new KernelError("EBADF");
349
+ },
350
+ resize: async () => {
351
+ throw new KernelError("EBADF");
352
+ },
353
+ stat: async () => ({ path, size: data.length }),
354
+ close: () => {
355
+ }
356
+ });
357
+ }
332
358
  function sinkFile(onData) {
333
359
  return new OpenFile({
334
360
  write: async (bytes) => {
@@ -5866,179 +5892,6 @@ function attachSyncSabResponder(port, sab, token, opts) {
5866
5892
  };
5867
5893
  }
5868
5894
 
5869
- // src/kernel/host-ops.ts
5870
- var HOST_OPS = [
5871
- "lock",
5872
- "unlock",
5873
- "async-submit",
5874
- "async-wait",
5875
- "async-take",
5876
- "async-resolve",
5877
- "async-hold",
5878
- "async-cancel",
5879
- "async-close"
5880
- ];
5881
- var json3 = (value) => ({ ok: true, kind: "json", json: value });
5882
- var done2 = { ok: true, kind: "void" };
5883
- var LockTable = class {
5884
- held = /* @__PURE__ */ new Map();
5885
- lock(pid, path, exclusive, fd) {
5886
- const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
5887
- for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
5888
- const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
5889
- mine.exclusive = exclusive;
5890
- mine.fds.add(fd);
5891
- holders.set(pid, mine);
5892
- this.held.set(path, holders);
5893
- return true;
5894
- }
5895
- unlock(pid, path) {
5896
- const holders = this.held.get(path);
5897
- holders?.delete(pid);
5898
- if (holders?.size === 0) this.held.delete(path);
5899
- }
5900
- closed(pid, fd) {
5901
- for (const [path, holders] of [...this.held]) {
5902
- const mine = holders.get(pid);
5903
- if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
5904
- }
5905
- }
5906
- release(pid) {
5907
- for (const path of [...this.held.keys()]) this.unlock(pid, path);
5908
- }
5909
- };
5910
- var AsyncOps = class {
5911
- next = 1;
5912
- results = /* @__PURE__ */ new Map();
5913
- ready = [];
5914
- running = /* @__PURE__ */ new Map();
5915
- waiters = /* @__PURE__ */ new Set();
5916
- closed = false;
5917
- run;
5918
- onReady;
5919
- constructor(run2, onReady) {
5920
- this.run = run2;
5921
- this.onReady = onReady;
5922
- }
5923
- submit(req) {
5924
- const id2 = this.hold();
5925
- const running = new AbortController();
5926
- this.running.set(id2, running);
5927
- this.run(req, running.signal).then(
5928
- (result) => this.complete(id2, result),
5929
- (err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
5930
- );
5931
- return id2;
5932
- }
5933
- resolve(value) {
5934
- const id2 = this.hold();
5935
- this.complete(id2, json3(value));
5936
- return id2;
5937
- }
5938
- hold() {
5939
- const id2 = this.next++;
5940
- this.results.set(id2, void 0);
5941
- return id2;
5942
- }
5943
- take(id2) {
5944
- if (!this.results.has(id2)) throw new KernelError("EINVAL");
5945
- const result = this.results.get(id2);
5946
- if (!result) throw new KernelError("EAGAIN");
5947
- this.forget(id2);
5948
- return result;
5949
- }
5950
- cancel(id2) {
5951
- this.running.get(id2)?.abort();
5952
- this.forget(id2);
5953
- }
5954
- forget(id2) {
5955
- this.running.delete(id2);
5956
- this.results.delete(id2);
5957
- const at = this.ready.indexOf(id2);
5958
- if (at >= 0) this.ready.splice(at, 1);
5959
- }
5960
- async wait(timeoutMs, signal) {
5961
- for (; ; ) {
5962
- if (this.closed) return -1;
5963
- const id2 = this.ready.shift();
5964
- if (id2 !== void 0) return id2;
5965
- if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
5966
- const woke = await this.sleep(timeoutMs, signal);
5967
- if (!woke) return 0;
5968
- }
5969
- }
5970
- close() {
5971
- this.closed = true;
5972
- this.wake();
5973
- }
5974
- sleep(timeoutMs, signal) {
5975
- return new Promise((resolve, reject) => {
5976
- let timer;
5977
- const cleanup = () => {
5978
- clearTimeout(timer);
5979
- this.waiters.delete(wake);
5980
- signal.removeEventListener("abort", abort);
5981
- };
5982
- const wake = () => {
5983
- cleanup();
5984
- resolve(true);
5985
- };
5986
- const abort = () => {
5987
- cleanup();
5988
- reject(new KernelError("EINTR"));
5989
- };
5990
- this.waiters.add(wake);
5991
- signal.addEventListener("abort", abort, { once: true });
5992
- if (timeoutMs !== void 0) {
5993
- timer = setTimeout(() => {
5994
- cleanup();
5995
- resolve(false);
5996
- }, timeoutMs);
5997
- }
5998
- });
5999
- }
6000
- complete(id2, result) {
6001
- this.running.delete(id2);
6002
- if (!this.results.has(id2)) return;
6003
- this.results.set(id2, result);
6004
- this.ready.push(id2);
6005
- this.wake();
6006
- this.onReady?.();
6007
- }
6008
- wake() {
6009
- for (const wake of [...this.waiters]) wake();
6010
- }
6011
- };
6012
- async function hostSyscall(req, ctx) {
6013
- switch (req.op) {
6014
- case "lock":
6015
- if (!ctx.locks) throw new KernelError("ENOSYS");
6016
- if (!ctx.locks.lock(ctx.pid, req.path, req.exclusive, req.fd))
6017
- throw new KernelError("EAGAIN");
6018
- return done2;
6019
- case "unlock":
6020
- ctx.locks?.unlock(ctx.pid, req.path);
6021
- return done2;
6022
- case "async-submit":
6023
- if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
6024
- return json3(ctx.ops.submit(req.req));
6025
- case "async-wait":
6026
- return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking()));
6027
- case "async-take":
6028
- return ctx.ops.take(req.id);
6029
- case "async-resolve":
6030
- return json3(ctx.ops.resolve(req.value));
6031
- case "async-hold":
6032
- return json3(ctx.ops.hold());
6033
- case "async-cancel":
6034
- ctx.ops.cancel(req.id);
6035
- return done2;
6036
- case "async-close":
6037
- ctx.ops.close();
6038
- return done2;
6039
- }
6040
- }
6041
-
6042
5895
  // src/kernel/net/loopback-names.ts
6043
5896
  var HOST_LOOPBACK = "host.slicc.internal";
6044
5897
  var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
@@ -6066,67 +5919,818 @@ function ownNames(hostname) {
6066
5919
  return [.../* @__PURE__ */ new Set([hostname.toLowerCase(), ...LIBC_HOSTNAMES])];
6067
5920
  }
6068
5921
 
6069
- // src/kernel/net/proxy-service.ts
6070
- var SEND_BUFFER2 = 1024 * 1024;
6071
- var TLS_HANDSHAKE = 22;
6072
- var TOKEN2 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
6073
- var NO_WEBSOCKET2 = "WebSocket not supported by this transport (fetchTransport needs { webSocket: true })";
6074
- var REALM_PROXY_PORT = 3128;
6075
- var DEFAULT_LIMITS = {
6076
- maxConnections: 64,
6077
- maxHead: 64 * 1024,
6078
- idleMs: 12e4,
6079
- bodyBudget: 128 * 1024 * 1024,
6080
- closeWaitMs: 5e3
6081
- };
6082
- var HOP_BY_HOP = /* @__PURE__ */ new Set([
6083
- "connection",
6084
- "keep-alive",
6085
- "proxy-connection",
6086
- "proxy-authenticate",
6087
- "proxy-authorization",
6088
- "te",
6089
- "trailer",
6090
- "transfer-encoding",
6091
- "upgrade"
6092
- ]);
6093
- var REQUEST_OWN = /* @__PURE__ */ new Set(["host", "content-length", "expect"]);
6094
- var Budget = class {
6095
- free;
6096
- waiters = [];
6097
- constructor(free) {
6098
- this.free = free;
5922
+ // src/process/process-fds.ts
5923
+ var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
5924
+ function mountTable(mounts = []) {
5925
+ const root = globalThis.navigator?.storage ? "opfs" : "memory";
5926
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
5927
+ for (const m of mounts) {
5928
+ const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
5929
+ if (m.state !== "ok") options.push(m.state);
5930
+ const access = options.includes("ro") ? [] : ["rw"];
5931
+ lines2.push(
5932
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
5933
+ );
6099
5934
  }
6100
- async acquire(n, signal) {
6101
- while (this.free < n) {
6102
- signal.throwIfAborted();
6103
- await new Promise((resolve) => {
6104
- const done4 = () => {
6105
- signal.removeEventListener("abort", done4);
6106
- resolve();
6107
- };
6108
- this.waiters.push(done4);
6109
- signal.addEventListener("abort", done4, { once: true });
6110
- });
6111
- }
6112
- this.free -= n;
5935
+ return `${lines2.join("\n")}
5936
+ `;
5937
+ }
5938
+
5939
+ // src/process/procfs.ts
5940
+ var HZ = 100;
5941
+ var KIB = 1024;
5942
+ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
5943
+ var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
5944
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
5945
+ function commOf(info) {
5946
+ const name2 = info.argv[0] ?? "";
5947
+ return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
5948
+ }
5949
+ function ttyNumber(tty) {
5950
+ const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
5951
+ if (pts) return 136 << 8 | Number(pts[1]);
5952
+ const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
5953
+ return vt ? 4 << 8 | Number(vt[1]) : 0;
5954
+ }
5955
+ function pages(bytes) {
5956
+ return Math.ceil(bytes / 4096);
5957
+ }
5958
+ function ticks(ms) {
5959
+ return Math.max(0, Math.floor(ms * HZ / 1e3));
5960
+ }
5961
+ function pidStat(info, boot) {
5962
+ const fields = [
5963
+ info.pid,
5964
+ `(${commOf(info)})`,
5965
+ info.state,
5966
+ info.ppid,
5967
+ info.pgid,
5968
+ info.sid,
5969
+ ttyNumber(info.tty),
5970
+ info.tty ? info.pgid : -1,
5971
+ 4194304,
5972
+ 0,
5973
+ 0,
5974
+ 0,
5975
+ 0,
5976
+ 0,
5977
+ 0,
5978
+ 0,
5979
+ 0,
5980
+ 20,
5981
+ 0,
5982
+ 1,
5983
+ 0,
5984
+ ticks(info.started - boot),
5985
+ info.memory,
5986
+ pages(info.memory),
5987
+ "18446744073709551615",
5988
+ ...Array(12).fill(0),
5989
+ 17,
5990
+ 0,
5991
+ 0,
5992
+ 0,
5993
+ 0,
5994
+ 0,
5995
+ 0,
5996
+ ...Array(7).fill(0),
5997
+ 0
5998
+ ];
5999
+ return `${fields.join(" ")}
6000
+ `;
6001
+ }
6002
+ var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
6003
+ function pidStatus(info) {
6004
+ const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
6005
+ const c = info.cred ?? ROOT;
6006
+ const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
6007
+ return [
6008
+ `Name: ${commOf(info)}`,
6009
+ `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
6010
+ `State: ${state}`,
6011
+ `Tgid: ${info.pid}`,
6012
+ "Ngid: 0",
6013
+ `Pid: ${info.pid}`,
6014
+ `PPid: ${info.ppid}`,
6015
+ "TracerPid: 0",
6016
+ `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
6017
+ `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
6018
+ "FDSize: 64",
6019
+ `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
6020
+ `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
6021
+ `Groups: ${c.groups.join(" ")}`,
6022
+ `NSpid: ${info.pid}`,
6023
+ `NSpgid: ${info.pgid}`,
6024
+ `NSsid: ${info.sid}`,
6025
+ "Threads: 1",
6026
+ "SigQ: 0/0",
6027
+ "SigPnd: 0000000000000000",
6028
+ "ShdPnd: 0000000000000000",
6029
+ "SigBlk: 0000000000000000",
6030
+ `SigIgn: ${sigMask(info.sigIgn)}`,
6031
+ `SigCgt: ${sigMask(info.sigCgt)}`,
6032
+ ""
6033
+ ].join("\n");
6034
+ }
6035
+ function pidFile(name2, info, boot) {
6036
+ if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
6037
+ if (name2 === "comm") return `${commOf(info)}
6038
+ `;
6039
+ if (name2 === "stat") return pidStat(info, boot);
6040
+ if (name2 === "statm") {
6041
+ const p = pages(info.memory);
6042
+ return `${p} ${p} 0 0 0 ${p} 0
6043
+ `;
6113
6044
  }
6114
- release(n) {
6115
- this.free += n;
6116
- const waiters = this.waiters;
6117
- this.waiters = [];
6118
- for (const wake of waiters) wake();
6045
+ return pidStatus(info);
6046
+ }
6047
+ function memoryInfo(source, used = 0) {
6048
+ const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
6049
+ const free = Math.max(0, total - used - (source.usedHeap ?? 0));
6050
+ const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
6051
+ return [
6052
+ ["MemTotal", kb(total)],
6053
+ ["MemFree", kb(free)],
6054
+ ["MemAvailable", kb(free)],
6055
+ ["Buffers", kb(0)],
6056
+ ["Cached", kb(0)],
6057
+ ["SwapCached", kb(0)],
6058
+ ["Shmem", kb(0)],
6059
+ ["SReclaimable", kb(0)],
6060
+ ["SwapTotal", kb(0)],
6061
+ ["SwapFree", kb(0)]
6062
+ ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
6063
+ }
6064
+ function systemFile(name2, listing, now, memory) {
6065
+ const up = Math.max(0, now - listing.boot) / 1e3;
6066
+ if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
6067
+ `;
6068
+ if (name2 === "meminfo") {
6069
+ return memoryInfo(
6070
+ memory,
6071
+ listing.processes.reduce((sum, p) => sum + p.memory, 0)
6072
+ );
6119
6073
  }
6120
- };
6121
- function canonicalHost2(host) {
6122
- const bare = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
6123
- try {
6124
- return new URL(`http://${bare}/`).hostname;
6125
- } catch {
6126
- return void 0;
6074
+ const live = listing.processes.filter((p) => p.state !== "Z");
6075
+ if (name2 === "loadavg") {
6076
+ const last = Math.max(0, ...listing.processes.map((p) => p.pid));
6077
+ return `0.00 0.00 0.00 1/${live.length} ${last}
6078
+ `;
6127
6079
  }
6080
+ const idle = ticks(now - listing.boot);
6081
+ return [
6082
+ `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
6083
+ `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
6084
+ "intr 0",
6085
+ "ctxt 0",
6086
+ `btime ${Math.floor(listing.boot / 1e3)}`,
6087
+ `processes ${listing.processes.length}`,
6088
+ "procs_running 1",
6089
+ "procs_blocked 0",
6090
+ ""
6091
+ ].join("\n");
6092
+ }
6093
+ function memorySource(scope = globalThis) {
6094
+ const nav = scope.navigator;
6095
+ const perf = scope.performance;
6096
+ const deviceMemory = nav?.deviceMemory;
6097
+ const used = perf?.memory?.usedJSHeapSize;
6098
+ return {
6099
+ ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
6100
+ ...typeof used === "number" ? { usedHeap: used } : {}
6101
+ };
6128
6102
  }
6129
- function localV4(a, b) {
6103
+
6104
+ // src/fs/pages.ts
6105
+ var PAGE = 64 * 1024;
6106
+ var READAHEAD = 1024 * 1024;
6107
+ var CLEAN_LIMIT = 4 * 1024 * 1024;
6108
+ var WRITEBACK = 8 * 1024 * 1024;
6109
+ var RUN = 1024 * 1024;
6110
+ var Pages = class {
6111
+ length;
6112
+ stored;
6113
+ shrink;
6114
+ pages = /* @__PURE__ */ new Map();
6115
+ dirty = /* @__PURE__ */ new Set();
6116
+ free = [];
6117
+ pinned = false;
6118
+ constructor(size) {
6119
+ this.length = size;
6120
+ this.stored = size;
6121
+ }
6122
+ get dirtyBytes() {
6123
+ return this.dirty.size * PAGE;
6124
+ }
6125
+ get changed() {
6126
+ return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
6127
+ }
6128
+ missing(at, length2) {
6129
+ this.evict();
6130
+ const end = Math.min(this.stored, at + length2, this.length);
6131
+ const runs = [];
6132
+ for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
6133
+ if (this.pages.has(page)) continue;
6134
+ const last2 = runs[runs.length - 1];
6135
+ if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
6136
+ else runs.push([page * PAGE, PAGE]);
6137
+ }
6138
+ const last = runs[runs.length - 1];
6139
+ if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
6140
+ return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
6141
+ }
6142
+ partial(at, length2) {
6143
+ if (length2 === 0) return [];
6144
+ this.evict();
6145
+ const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
6146
+ const runs = [];
6147
+ for (const page of edges) {
6148
+ const start2 = page * PAGE;
6149
+ const covered = at <= start2 && at + length2 >= start2 + PAGE;
6150
+ if (covered || this.pages.has(page) || start2 >= this.stored) continue;
6151
+ runs.push([start2, Math.min(PAGE, this.stored - start2)]);
6152
+ }
6153
+ return runs;
6154
+ }
6155
+ fill(start2, bytes) {
6156
+ for (let at = 0; at < bytes.length; at += PAGE) {
6157
+ const page = (start2 + at) / PAGE;
6158
+ if (this.pages.has(page)) continue;
6159
+ const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
6160
+ const fresh = this.take(part.length);
6161
+ fresh.set(part);
6162
+ this.pages.set(page, fresh);
6163
+ }
6164
+ }
6165
+ read(at, length2) {
6166
+ const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
6167
+ this.readInto(at, out);
6168
+ return out;
6169
+ }
6170
+ readInto(at, out) {
6171
+ const end = Math.min(this.length, at + out.length);
6172
+ for (let pos = at; pos < end; ) {
6173
+ const page = Math.floor(pos / PAGE);
6174
+ const from = pos - page * PAGE;
6175
+ const n = Math.min(PAGE - from, end - pos);
6176
+ const bytes = this.pages.get(page);
6177
+ if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
6178
+ else out.fill(0, pos - at, pos - at + n);
6179
+ pos += n;
6180
+ }
6181
+ return Math.max(0, end - at);
6182
+ }
6183
+ write(at, bytes) {
6184
+ for (let done4 = 0; done4 < bytes.length; ) {
6185
+ const pos = at + done4;
6186
+ const page = Math.floor(pos / PAGE);
6187
+ const from = pos - page * PAGE;
6188
+ const n = Math.min(PAGE - from, bytes.length - done4);
6189
+ let target = this.pages.get(page);
6190
+ if (!target) {
6191
+ target = this.take(0);
6192
+ this.pages.set(page, target);
6193
+ }
6194
+ target.set(bytes.subarray(done4, done4 + n), from);
6195
+ this.dirty.add(page);
6196
+ done4 += n;
6197
+ }
6198
+ this.length = Math.max(this.length, at + bytes.length);
6199
+ }
6200
+ truncate(size) {
6201
+ if (size < this.stored) {
6202
+ this.stored = size;
6203
+ this.shrink = Math.min(this.shrink ?? size, size);
6204
+ }
6205
+ if (size < this.length) {
6206
+ const edge = Math.floor(size / PAGE);
6207
+ for (const page of [...this.pages.keys()]) {
6208
+ if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
6209
+ }
6210
+ this.pages.get(edge)?.fill(0, size - edge * PAGE);
6211
+ }
6212
+ this.length = size;
6213
+ }
6214
+ plan() {
6215
+ const runs = [];
6216
+ const sorted = [...this.dirty].sort((a, b) => a - b);
6217
+ for (let i = 0; i < sorted.length; ) {
6218
+ let j = i + 1;
6219
+ const first = sorted[i];
6220
+ while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
6221
+ j++;
6222
+ }
6223
+ const start2 = first * PAGE;
6224
+ const end = Math.min(this.length, (first + (j - i)) * PAGE);
6225
+ const bytes = new Uint8Array(end - start2);
6226
+ for (let k = i; k < j; k++) {
6227
+ const page = this.pages.get(sorted[k]);
6228
+ bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
6229
+ }
6230
+ runs.push({ offset: start2, bytes });
6231
+ i = j;
6232
+ }
6233
+ const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
6234
+ return {
6235
+ ...this.shrink !== void 0 ? { shrink: this.shrink } : {},
6236
+ runs,
6237
+ ...this.length > written2 ? { extend: this.length } : {}
6238
+ };
6239
+ }
6240
+ flushed() {
6241
+ this.dirty.clear();
6242
+ this.shrink = void 0;
6243
+ this.stored = this.length;
6244
+ this.evict();
6245
+ }
6246
+ pin() {
6247
+ this.pinned = true;
6248
+ }
6249
+ reset(size) {
6250
+ if (this.pinned) return;
6251
+ for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
6252
+ if (this.changed) return;
6253
+ this.length = size;
6254
+ this.stored = size;
6255
+ }
6256
+ evict() {
6257
+ if (this.pinned) return;
6258
+ let clean = (this.pages.size - this.dirty.size) * PAGE;
6259
+ for (const page of this.pages.keys()) {
6260
+ if (clean <= CLEAN_LIMIT) return;
6261
+ if (this.dirty.has(page)) continue;
6262
+ this.drop(page);
6263
+ clean -= PAGE;
6264
+ }
6265
+ }
6266
+ take(used) {
6267
+ const page = this.free.pop();
6268
+ if (!page) return new Uint8Array(PAGE);
6269
+ page.fill(0, used);
6270
+ return page;
6271
+ }
6272
+ drop(page) {
6273
+ const bytes = this.pages.get(page);
6274
+ this.pages.delete(page);
6275
+ this.dirty.delete(page);
6276
+ if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
6277
+ }
6278
+ };
6279
+
6280
+ // src/fs/ranged.ts
6281
+ var AsyncRangedFile = class {
6282
+ pages;
6283
+ io;
6284
+ pinned = false;
6285
+ version;
6286
+ path;
6287
+ constructor(io, path, size, version) {
6288
+ this.io = io;
6289
+ this.path = path;
6290
+ this.version = version;
6291
+ this.pages = new Pages(size);
6292
+ }
6293
+ size() {
6294
+ return this.pages.length;
6295
+ }
6296
+ async fetch(runs) {
6297
+ for (const [start2, n] of runs) {
6298
+ this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
6299
+ }
6300
+ }
6301
+ async read(at, length2) {
6302
+ await this.fetch(this.pages.missing(at, length2));
6303
+ return this.pages.read(at, length2);
6304
+ }
6305
+ async write(at, bytes) {
6306
+ await this.fetch(this.pages.partial(at, bytes.length));
6307
+ this.pages.write(at, bytes);
6308
+ if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
6309
+ }
6310
+ truncate(size) {
6311
+ this.pages.truncate(size);
6312
+ }
6313
+ async flush() {
6314
+ if (this.pinned || !this.pages.changed) return;
6315
+ const plan = this.pages.plan();
6316
+ if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
6317
+ for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
6318
+ if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
6319
+ this.version = void 0;
6320
+ this.pages.flushed();
6321
+ }
6322
+ async pin() {
6323
+ this.pinned = true;
6324
+ this.pages.pin();
6325
+ await this.fetch(this.pages.missing(0, this.pages.length));
6326
+ }
6327
+ };
6328
+
6329
+ // src/process/wasi/wasi-files.ts
6330
+ function normalize(path) {
6331
+ const out = [];
6332
+ for (const seg of path.split("/")) {
6333
+ if (seg === "" || seg === ".") continue;
6334
+ if (seg === "..") out.pop();
6335
+ else out.push(seg);
6336
+ }
6337
+ return `/${out.join("/")}`;
6338
+ }
6339
+
6340
+ // src/process/wasi/wasi-procfs.ts
6341
+ var DIR_MODE = 16749;
6342
+ var FILE_MODE = 33060;
6343
+ var LINK_MODE = 41471;
6344
+ var PID_NAMES = PID_FILES;
6345
+ var SYSTEM_NAMES = SYSTEM_FILES;
6346
+ function inProc(path) {
6347
+ return path === "/proc" || path.startsWith("/proc/");
6348
+ }
6349
+ var ProcTree = class {
6350
+ source;
6351
+ pid;
6352
+ memory;
6353
+ constructor(source, pid, memory = memorySource()) {
6354
+ this.source = source;
6355
+ this.pid = pid;
6356
+ this.memory = memory;
6357
+ }
6358
+ listing() {
6359
+ return this.source.procList();
6360
+ }
6361
+ alive(pid) {
6362
+ return this.listing().processes.some((p) => p.pid === pid);
6363
+ }
6364
+ node(parts) {
6365
+ const [top, leaf, ...rest2] = parts;
6366
+ if (top === void 0) {
6367
+ return {
6368
+ kind: "dir",
6369
+ names: () => [
6370
+ ...SYSTEM_NAMES,
6371
+ "mounts",
6372
+ "self",
6373
+ ...this.listing().processes.map((p) => String(p.pid))
6374
+ ]
6375
+ };
6376
+ }
6377
+ if (top === "self") {
6378
+ return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
6379
+ }
6380
+ if (leaf === void 0 && top === "mounts") {
6381
+ return { kind: "file", content: () => mountTable(this.source.mountList()) };
6382
+ }
6383
+ if (leaf === void 0 && SYSTEM_NAMES.includes(top)) {
6384
+ return {
6385
+ kind: "file",
6386
+ content: () => systemFile(top, this.listing(), Date.now(), this.memory)
6387
+ };
6388
+ }
6389
+ if (!/^[1-9]\d*$/.test(top) || rest2.length > 0 || !this.alive(Number(top))) return void 0;
6390
+ if (leaf === void 0) return { kind: "dir", names: () => [...PID_NAMES] };
6391
+ if (!PID_NAMES.includes(leaf)) return void 0;
6392
+ return {
6393
+ kind: "file",
6394
+ content: () => {
6395
+ const listing = this.listing();
6396
+ const info = listing.processes.find((p) => p.pid === Number(top));
6397
+ if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
6398
+ return pidFile(leaf, info, listing.boot);
6399
+ }
6400
+ };
6401
+ }
6402
+ parts(path, follow2) {
6403
+ const parts = path.split("/").filter((p) => p !== "").slice(1);
6404
+ if (parts[0] === "self" && (follow2 || parts.length > 1)) parts[0] = String(this.pid);
6405
+ return parts;
6406
+ }
6407
+ find(path, follow2) {
6408
+ const node = this.node(this.parts(path, follow2));
6409
+ if (!node) throw syncError("ENOENT", path);
6410
+ return node;
6411
+ }
6412
+ stat(path, follow2 = true) {
6413
+ const node = this.find(path, follow2);
6414
+ const base = {
6415
+ isFile: false,
6416
+ isDirectory: false,
6417
+ size: 0,
6418
+ mtimeMs: Date.now(),
6419
+ readonly: true
6420
+ };
6421
+ if (node.kind === "dir") return { ...base, isDirectory: true, mode: DIR_MODE };
6422
+ if (node.kind === "link") {
6423
+ return { ...base, isSymbolicLink: true, mode: LINK_MODE, size: node.target.length };
6424
+ }
6425
+ return { ...base, isFile: true, mode: FILE_MODE, size: this.bytes(node).length };
6426
+ }
6427
+ bytes(node) {
6428
+ return new TextEncoder().encode(node.kind === "file" ? node.content() : "");
6429
+ }
6430
+ read(path) {
6431
+ const node = this.find(path, true);
6432
+ if (node.kind !== "file") throw syncError("EISDIR", path);
6433
+ return this.bytes(node);
6434
+ }
6435
+ readdir(path) {
6436
+ const node = this.find(path, true);
6437
+ if (node.kind !== "dir") throw syncError("ENOTDIR", path);
6438
+ return node.names();
6439
+ }
6440
+ readlink(path) {
6441
+ const node = this.find(path, false);
6442
+ if (node.kind !== "link") throw syncError("EINVAL", path);
6443
+ return node.target;
6444
+ }
6445
+ exists(path) {
6446
+ try {
6447
+ this.find(path, true);
6448
+ return true;
6449
+ } catch {
6450
+ return false;
6451
+ }
6452
+ }
6453
+ };
6454
+ var MAX_HOPS2 = 40;
6455
+ function* procWalk(path, follow2) {
6456
+ let done4 = [];
6457
+ let todo = path.split("/").filter((part) => part !== "");
6458
+ let hops = 0;
6459
+ while (todo.length > 0) {
6460
+ const part = todo.shift();
6461
+ if (part === ".") continue;
6462
+ if (part === "..") {
6463
+ done4.pop();
6464
+ continue;
6465
+ }
6466
+ const at = `/${[...done4, part].join("/")}`;
6467
+ const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
6468
+ if (inProc(at) && inProc(whole)) return whole;
6469
+ if (!follow2 && todo.length === 0) return void 0;
6470
+ const target = yield at;
6471
+ if (target === null) return void 0;
6472
+ if (target === void 0) done4.push(part);
6473
+ else {
6474
+ if (++hops > MAX_HOPS2) return void 0;
6475
+ if (target.startsWith("/")) done4 = [];
6476
+ todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
6477
+ }
6478
+ }
6479
+ return void 0;
6480
+ }
6481
+ async function procAlias(fs, path, follow2) {
6482
+ const { readlink, lstat } = fs;
6483
+ if (!readlink || !lstat) return void 0;
6484
+ const physical = await lstat.call(fs, "/proc").then(
6485
+ () => true,
6486
+ () => false
6487
+ );
6488
+ if (!physical) return void 0;
6489
+ const walk = procWalk(path, follow2);
6490
+ let step = walk.next();
6491
+ while (!step.done) {
6492
+ const target = await readlink.call(fs, step.value).catch(() => void 0);
6493
+ step = walk.next(target);
6494
+ }
6495
+ return step.value;
6496
+ }
6497
+
6498
+ // src/kernel/host-ops.ts
6499
+ var HOST_OPS = [
6500
+ "lock",
6501
+ "unlock",
6502
+ "async-submit",
6503
+ "async-wait",
6504
+ "async-take",
6505
+ "async-resolve",
6506
+ "async-hold",
6507
+ "async-cancel",
6508
+ "async-close"
6509
+ ];
6510
+ var json3 = (value) => ({ ok: true, kind: "json", json: value });
6511
+ var done2 = { ok: true, kind: "void" };
6512
+ var LockTable = class {
6513
+ held = /* @__PURE__ */ new Map();
6514
+ lock(pid, path, exclusive, fd) {
6515
+ const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
6516
+ for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
6517
+ const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
6518
+ mine.exclusive = exclusive;
6519
+ mine.fds.add(fd);
6520
+ holders.set(pid, mine);
6521
+ this.held.set(path, holders);
6522
+ return true;
6523
+ }
6524
+ unlock(pid, path) {
6525
+ const holders = this.held.get(path);
6526
+ holders?.delete(pid);
6527
+ if (holders?.size === 0) this.held.delete(path);
6528
+ }
6529
+ closed(pid, fd) {
6530
+ for (const [path, holders] of [...this.held]) {
6531
+ const mine = holders.get(pid);
6532
+ if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
6533
+ }
6534
+ }
6535
+ release(pid) {
6536
+ for (const path of [...this.held.keys()]) this.unlock(pid, path);
6537
+ }
6538
+ };
6539
+ var AsyncOps = class {
6540
+ next = 1;
6541
+ results = /* @__PURE__ */ new Map();
6542
+ ready = [];
6543
+ running = /* @__PURE__ */ new Map();
6544
+ waiters = /* @__PURE__ */ new Set();
6545
+ closed = false;
6546
+ run;
6547
+ onReady;
6548
+ constructor(run2, onReady) {
6549
+ this.run = run2;
6550
+ this.onReady = onReady;
6551
+ }
6552
+ submit(req) {
6553
+ const id2 = this.hold();
6554
+ const running = new AbortController();
6555
+ this.running.set(id2, running);
6556
+ this.run(req, running.signal).then(
6557
+ (result) => this.complete(id2, result),
6558
+ (err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
6559
+ );
6560
+ return id2;
6561
+ }
6562
+ resolve(value) {
6563
+ const id2 = this.hold();
6564
+ this.complete(id2, json3(value));
6565
+ return id2;
6566
+ }
6567
+ hold() {
6568
+ const id2 = this.next++;
6569
+ this.results.set(id2, void 0);
6570
+ return id2;
6571
+ }
6572
+ take(id2) {
6573
+ if (!this.results.has(id2)) throw new KernelError("EINVAL");
6574
+ const result = this.results.get(id2);
6575
+ if (!result) throw new KernelError("EAGAIN");
6576
+ this.forget(id2);
6577
+ return result;
6578
+ }
6579
+ cancel(id2) {
6580
+ this.running.get(id2)?.abort();
6581
+ this.forget(id2);
6582
+ }
6583
+ forget(id2) {
6584
+ this.running.delete(id2);
6585
+ this.results.delete(id2);
6586
+ const at = this.ready.indexOf(id2);
6587
+ if (at >= 0) this.ready.splice(at, 1);
6588
+ }
6589
+ async wait(timeoutMs, blocking) {
6590
+ let signal;
6591
+ for (; ; ) {
6592
+ if (this.closed) return -1;
6593
+ const id2 = this.ready.shift();
6594
+ if (id2 !== void 0) return id2;
6595
+ if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
6596
+ signal ??= blocking();
6597
+ const woke = await this.sleep(timeoutMs, signal);
6598
+ if (!woke) return 0;
6599
+ }
6600
+ }
6601
+ close() {
6602
+ this.closed = true;
6603
+ this.wake();
6604
+ }
6605
+ sleep(timeoutMs, signal) {
6606
+ return new Promise((resolve, reject) => {
6607
+ let timer;
6608
+ const cleanup = () => {
6609
+ clearTimeout(timer);
6610
+ this.waiters.delete(wake);
6611
+ signal.removeEventListener("abort", abort);
6612
+ };
6613
+ const wake = () => {
6614
+ cleanup();
6615
+ resolve(true);
6616
+ };
6617
+ const abort = () => {
6618
+ cleanup();
6619
+ reject(new KernelError("EINTR"));
6620
+ };
6621
+ this.waiters.add(wake);
6622
+ signal.addEventListener("abort", abort, { once: true });
6623
+ if (timeoutMs !== void 0) {
6624
+ timer = setTimeout(() => {
6625
+ cleanup();
6626
+ resolve(false);
6627
+ }, timeoutMs);
6628
+ }
6629
+ });
6630
+ }
6631
+ complete(id2, result) {
6632
+ this.running.delete(id2);
6633
+ if (!this.results.has(id2)) return;
6634
+ this.results.set(id2, result);
6635
+ this.ready.push(id2);
6636
+ this.wake();
6637
+ this.onReady?.();
6638
+ }
6639
+ wake() {
6640
+ for (const wake of [...this.waiters]) wake();
6641
+ }
6642
+ };
6643
+ async function hostSyscall(req, ctx) {
6644
+ switch (req.op) {
6645
+ case "lock":
6646
+ if (!ctx.locks) throw new KernelError("ENOSYS");
6647
+ if (!ctx.locks.lock(ctx.pid, req.path, req.exclusive, req.fd))
6648
+ throw new KernelError("EAGAIN");
6649
+ return done2;
6650
+ case "unlock":
6651
+ ctx.locks?.unlock(ctx.pid, req.path);
6652
+ return done2;
6653
+ case "async-submit":
6654
+ if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
6655
+ return json3(ctx.ops.submit(req.req));
6656
+ case "async-wait":
6657
+ return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
6658
+ case "async-take":
6659
+ return ctx.ops.take(req.id);
6660
+ case "async-resolve":
6661
+ return json3(ctx.ops.resolve(req.value));
6662
+ case "async-hold":
6663
+ return json3(ctx.ops.hold());
6664
+ case "async-cancel":
6665
+ ctx.ops.cancel(req.id);
6666
+ return done2;
6667
+ case "async-close":
6668
+ ctx.ops.close();
6669
+ return done2;
6670
+ }
6671
+ }
6672
+
6673
+ // src/kernel/net/proxy-service.ts
6674
+ var SEND_BUFFER2 = 1024 * 1024;
6675
+ var TLS_HANDSHAKE = 22;
6676
+ var TOKEN2 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
6677
+ var NO_WEBSOCKET2 = "WebSocket not supported by this transport (fetchTransport needs { webSocket: true })";
6678
+ var REALM_PROXY_PORT = 3128;
6679
+ var DEFAULT_LIMITS = {
6680
+ maxConnections: 64,
6681
+ maxHead: 64 * 1024,
6682
+ idleMs: 12e4,
6683
+ bodyBudget: 128 * 1024 * 1024,
6684
+ closeWaitMs: 5e3
6685
+ };
6686
+ var HOP_BY_HOP = /* @__PURE__ */ new Set([
6687
+ "connection",
6688
+ "keep-alive",
6689
+ "proxy-connection",
6690
+ "proxy-authenticate",
6691
+ "proxy-authorization",
6692
+ "te",
6693
+ "trailer",
6694
+ "transfer-encoding",
6695
+ "upgrade"
6696
+ ]);
6697
+ var REQUEST_OWN = /* @__PURE__ */ new Set(["host", "content-length", "expect"]);
6698
+ var Budget = class {
6699
+ free;
6700
+ waiters = [];
6701
+ constructor(free) {
6702
+ this.free = free;
6703
+ }
6704
+ async acquire(n, signal) {
6705
+ while (this.free < n) {
6706
+ signal.throwIfAborted();
6707
+ await new Promise((resolve) => {
6708
+ const done4 = () => {
6709
+ signal.removeEventListener("abort", done4);
6710
+ resolve();
6711
+ };
6712
+ this.waiters.push(done4);
6713
+ signal.addEventListener("abort", done4, { once: true });
6714
+ });
6715
+ }
6716
+ this.free -= n;
6717
+ }
6718
+ release(n) {
6719
+ this.free += n;
6720
+ const waiters = this.waiters;
6721
+ this.waiters = [];
6722
+ for (const wake of waiters) wake();
6723
+ }
6724
+ };
6725
+ function canonicalHost2(host) {
6726
+ const bare = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
6727
+ try {
6728
+ return new URL(`http://${bare}/`).hostname;
6729
+ } catch {
6730
+ return void 0;
6731
+ }
6732
+ }
6733
+ function localV4(a, b) {
6130
6734
  return a === 127 || a === 0 || a === 169 && b === 254;
6131
6735
  }
6132
6736
  function hextets(address) {
@@ -8259,343 +8863,118 @@ async function selectFds(fds, read, write, timeoutMs, interrupt) {
8259
8863
  try {
8260
8864
  return await waitReady(fds, read, write, deadline, interrupted2.promise);
8261
8865
  } finally {
8262
- interrupted2.done();
8263
- }
8264
- }
8265
- async function waitReady(fds, read, write, deadline, interrupted2) {
8266
- for (; ; ) {
8267
- const now = ready(fds, read, write);
8268
- if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
8269
- const round = new AbortController();
8270
- const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
8271
- const file = fds.get(fd).file;
8272
- return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
8273
- });
8274
- let timer;
8275
- const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
8276
- try {
8277
- await Promise.race([...changes, ...expiry, interrupted2]);
8278
- } finally {
8279
- round.abort();
8280
- clearTimeout(timer);
8281
- }
8282
- }
8283
- }
8284
-
8285
- // src/kernel/socket-syscalls.ts
8286
- var SOCKET_OPS = [
8287
- "sock-open",
8288
- "sock-pair",
8289
- "sock-bind",
8290
- "sock-listen",
8291
- "sock-accept",
8292
- "sock-connect",
8293
- "sock-shutdown",
8294
- "sock-name",
8295
- "sock-getopt",
8296
- "sock-setopt",
8297
- "sock-resolve"
8298
- ];
8299
- function socketAt(fds, fd) {
8300
- const file = fds.get(fd).file;
8301
- if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
8302
- return file;
8303
- }
8304
- var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
8305
- async function socketSyscall(req, proc) {
8306
- const { fds, net } = proc;
8307
- switch (req.op) {
8308
- case "sock-open":
8309
- return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
8310
- case "sock-pair": {
8311
- const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
8312
- const first = fds.install(new OpenFile(a), 3);
8313
- try {
8314
- return ok([first, fds.install(new OpenFile(b), 3)]);
8315
- } catch (e) {
8316
- await Promise.resolve(fds.close(first));
8317
- throw e;
8318
- }
8319
- }
8320
- case "sock-bind":
8321
- socketAt(fds, req.fd).bind(req.addr);
8322
- return ok();
8323
- case "sock-listen":
8324
- socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
8325
- return ok();
8326
- case "sock-accept":
8327
- return ok(await accept(proc, req.fd, req.nonblock));
8328
- case "sock-connect": {
8329
- const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
8330
- if (req.nonblock) {
8331
- dialing?.catch(() => void 0);
8332
- throw new KernelError("EINPROGRESS");
8333
- }
8334
- if (dialing !== void 0) await interruptible(dialing, proc.blocking());
8335
- return ok();
8336
- }
8337
- case "sock-shutdown":
8338
- socketAt(fds, req.fd).shutdown(req.how);
8339
- return ok();
8340
- case "sock-name": {
8341
- const socket = socketAt(fds, req.fd);
8342
- const addr = req.peer ? socket.peer : socket.local;
8343
- if (req.peer && !addr) throw new KernelError("ENOTCONN");
8344
- return ok(addr ?? unnamed(socket.domain));
8345
- }
8346
- case "sock-getopt":
8347
- return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
8348
- case "sock-setopt":
8349
- socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
8350
- return ok();
8351
- case "sock-resolve": {
8352
- const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
8353
- if (addresses.length === 0) throw new KernelError("ENOENT");
8354
- return ok(addresses);
8355
- }
8356
- }
8357
- }
8358
- function interruptible(dialing, signal) {
8359
- return new Promise((resolve, reject) => {
8360
- const abort = () => reject(new KernelError("EINTR"));
8361
- signal.addEventListener("abort", abort, { once: true });
8362
- dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
8363
- });
8364
- }
8365
- function unnamed(domain) {
8366
- return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
8367
- }
8368
- async function accept(proc, fd, nonblock) {
8369
- const listener = socketAt(proc.fds, fd);
8370
- const waits = !nonblock && !pollFile(listener).readable;
8371
- const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
8372
- return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
8373
- }
8374
-
8375
- // src/fs/pages.ts
8376
- var PAGE = 64 * 1024;
8377
- var READAHEAD = 1024 * 1024;
8378
- var CLEAN_LIMIT = 4 * 1024 * 1024;
8379
- var WRITEBACK = 8 * 1024 * 1024;
8380
- var RUN = 1024 * 1024;
8381
- var Pages = class {
8382
- length;
8383
- stored;
8384
- shrink;
8385
- pages = /* @__PURE__ */ new Map();
8386
- dirty = /* @__PURE__ */ new Set();
8387
- free = [];
8388
- pinned = false;
8389
- constructor(size) {
8390
- this.length = size;
8391
- this.stored = size;
8392
- }
8393
- get dirtyBytes() {
8394
- return this.dirty.size * PAGE;
8395
- }
8396
- get changed() {
8397
- return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
8398
- }
8399
- missing(at, length2) {
8400
- this.evict();
8401
- const end = Math.min(this.stored, at + length2, this.length);
8402
- const runs = [];
8403
- for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
8404
- if (this.pages.has(page)) continue;
8405
- const last2 = runs[runs.length - 1];
8406
- if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
8407
- else runs.push([page * PAGE, PAGE]);
8408
- }
8409
- const last = runs[runs.length - 1];
8410
- if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
8411
- return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
8412
- }
8413
- partial(at, length2) {
8414
- if (length2 === 0) return [];
8415
- this.evict();
8416
- const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
8417
- const runs = [];
8418
- for (const page of edges) {
8419
- const start2 = page * PAGE;
8420
- const covered = at <= start2 && at + length2 >= start2 + PAGE;
8421
- if (covered || this.pages.has(page) || start2 >= this.stored) continue;
8422
- runs.push([start2, Math.min(PAGE, this.stored - start2)]);
8423
- }
8424
- return runs;
8425
- }
8426
- fill(start2, bytes) {
8427
- for (let at = 0; at < bytes.length; at += PAGE) {
8428
- const page = (start2 + at) / PAGE;
8429
- if (this.pages.has(page)) continue;
8430
- const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
8431
- const fresh = this.take(part.length);
8432
- fresh.set(part);
8433
- this.pages.set(page, fresh);
8434
- }
8435
- }
8436
- read(at, length2) {
8437
- const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
8438
- this.readInto(at, out);
8439
- return out;
8440
- }
8441
- readInto(at, out) {
8442
- const end = Math.min(this.length, at + out.length);
8443
- for (let pos = at; pos < end; ) {
8444
- const page = Math.floor(pos / PAGE);
8445
- const from = pos - page * PAGE;
8446
- const n = Math.min(PAGE - from, end - pos);
8447
- const bytes = this.pages.get(page);
8448
- if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
8449
- else out.fill(0, pos - at, pos - at + n);
8450
- pos += n;
8451
- }
8452
- return Math.max(0, end - at);
8453
- }
8454
- write(at, bytes) {
8455
- for (let done4 = 0; done4 < bytes.length; ) {
8456
- const pos = at + done4;
8457
- const page = Math.floor(pos / PAGE);
8458
- const from = pos - page * PAGE;
8459
- const n = Math.min(PAGE - from, bytes.length - done4);
8460
- let target = this.pages.get(page);
8461
- if (!target) {
8462
- target = this.take(0);
8463
- this.pages.set(page, target);
8464
- }
8465
- target.set(bytes.subarray(done4, done4 + n), from);
8466
- this.dirty.add(page);
8467
- done4 += n;
8468
- }
8469
- this.length = Math.max(this.length, at + bytes.length);
8866
+ interrupted2.done();
8470
8867
  }
8471
- truncate(size) {
8472
- if (size < this.stored) {
8473
- this.stored = size;
8474
- this.shrink = Math.min(this.shrink ?? size, size);
8475
- }
8476
- if (size < this.length) {
8477
- const edge = Math.floor(size / PAGE);
8478
- for (const page of [...this.pages.keys()]) {
8479
- if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
8480
- }
8481
- this.pages.get(edge)?.fill(0, size - edge * PAGE);
8868
+ }
8869
+ async function waitReady(fds, read, write, deadline, interrupted2) {
8870
+ for (; ; ) {
8871
+ const now = ready(fds, read, write);
8872
+ if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
8873
+ const round = new AbortController();
8874
+ const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
8875
+ const file = fds.get(fd).file;
8876
+ return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
8877
+ });
8878
+ let timer;
8879
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
8880
+ try {
8881
+ await Promise.race([...changes, ...expiry, interrupted2]);
8882
+ } finally {
8883
+ round.abort();
8884
+ clearTimeout(timer);
8482
8885
  }
8483
- this.length = size;
8484
8886
  }
8485
- plan() {
8486
- const runs = [];
8487
- const sorted = [...this.dirty].sort((a, b) => a - b);
8488
- for (let i = 0; i < sorted.length; ) {
8489
- let j = i + 1;
8490
- const first = sorted[i];
8491
- while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
8492
- j++;
8887
+ }
8888
+
8889
+ // src/kernel/socket-syscalls.ts
8890
+ var SOCKET_OPS = [
8891
+ "sock-open",
8892
+ "sock-pair",
8893
+ "sock-bind",
8894
+ "sock-listen",
8895
+ "sock-accept",
8896
+ "sock-connect",
8897
+ "sock-shutdown",
8898
+ "sock-name",
8899
+ "sock-getopt",
8900
+ "sock-setopt",
8901
+ "sock-resolve"
8902
+ ];
8903
+ function socketAt(fds, fd) {
8904
+ const file = fds.get(fd).file;
8905
+ if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
8906
+ return file;
8907
+ }
8908
+ var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
8909
+ async function socketSyscall(req, proc) {
8910
+ const { fds, net } = proc;
8911
+ switch (req.op) {
8912
+ case "sock-open":
8913
+ return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
8914
+ case "sock-pair": {
8915
+ const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
8916
+ const first = fds.install(new OpenFile(a), 3);
8917
+ try {
8918
+ return ok([first, fds.install(new OpenFile(b), 3)]);
8919
+ } catch (e) {
8920
+ await Promise.resolve(fds.close(first));
8921
+ throw e;
8493
8922
  }
8494
- const start2 = first * PAGE;
8495
- const end = Math.min(this.length, (first + (j - i)) * PAGE);
8496
- const bytes = new Uint8Array(end - start2);
8497
- for (let k = i; k < j; k++) {
8498
- const page = this.pages.get(sorted[k]);
8499
- bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
8923
+ }
8924
+ case "sock-bind":
8925
+ socketAt(fds, req.fd).bind(req.addr);
8926
+ return ok();
8927
+ case "sock-listen":
8928
+ socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
8929
+ return ok();
8930
+ case "sock-accept":
8931
+ return ok(await accept(proc, req.fd, req.nonblock));
8932
+ case "sock-connect": {
8933
+ const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
8934
+ if (req.nonblock) {
8935
+ dialing?.catch(() => void 0);
8936
+ throw new KernelError("EINPROGRESS");
8500
8937
  }
8501
- runs.push({ offset: start2, bytes });
8502
- i = j;
8938
+ if (dialing !== void 0) await interruptible(dialing, proc.blocking());
8939
+ return ok();
8503
8940
  }
8504
- const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
8505
- return {
8506
- ...this.shrink !== void 0 ? { shrink: this.shrink } : {},
8507
- runs,
8508
- ...this.length > written2 ? { extend: this.length } : {}
8509
- };
8510
- }
8511
- flushed() {
8512
- this.dirty.clear();
8513
- this.shrink = void 0;
8514
- this.stored = this.length;
8515
- this.evict();
8516
- }
8517
- pin() {
8518
- this.pinned = true;
8519
- }
8520
- reset(size) {
8521
- if (this.pinned) return;
8522
- for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
8523
- if (this.changed) return;
8524
- this.length = size;
8525
- this.stored = size;
8526
- }
8527
- evict() {
8528
- if (this.pinned) return;
8529
- let clean = (this.pages.size - this.dirty.size) * PAGE;
8530
- for (const page of this.pages.keys()) {
8531
- if (clean <= CLEAN_LIMIT) return;
8532
- if (this.dirty.has(page)) continue;
8533
- this.drop(page);
8534
- clean -= PAGE;
8941
+ case "sock-shutdown":
8942
+ socketAt(fds, req.fd).shutdown(req.how);
8943
+ return ok();
8944
+ case "sock-name": {
8945
+ const socket = socketAt(fds, req.fd);
8946
+ const addr = req.peer ? socket.peer : socket.local;
8947
+ if (req.peer && !addr) throw new KernelError("ENOTCONN");
8948
+ return ok(addr ?? unnamed(socket.domain));
8535
8949
  }
8536
- }
8537
- take(used) {
8538
- const page = this.free.pop();
8539
- if (!page) return new Uint8Array(PAGE);
8540
- page.fill(0, used);
8541
- return page;
8542
- }
8543
- drop(page) {
8544
- const bytes = this.pages.get(page);
8545
- this.pages.delete(page);
8546
- this.dirty.delete(page);
8547
- if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
8548
- }
8549
- };
8550
-
8551
- // src/fs/ranged.ts
8552
- var AsyncRangedFile = class {
8553
- pages;
8554
- io;
8555
- pinned = false;
8556
- version;
8557
- path;
8558
- constructor(io, path, size, version) {
8559
- this.io = io;
8560
- this.path = path;
8561
- this.version = version;
8562
- this.pages = new Pages(size);
8563
- }
8564
- size() {
8565
- return this.pages.length;
8566
- }
8567
- async fetch(runs) {
8568
- for (const [start2, n] of runs) {
8569
- this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
8950
+ case "sock-getopt":
8951
+ return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
8952
+ case "sock-setopt":
8953
+ socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
8954
+ return ok();
8955
+ case "sock-resolve": {
8956
+ const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
8957
+ if (addresses.length === 0) throw new KernelError("ENOENT");
8958
+ return ok(addresses);
8570
8959
  }
8571
8960
  }
8572
- async read(at, length2) {
8573
- await this.fetch(this.pages.missing(at, length2));
8574
- return this.pages.read(at, length2);
8575
- }
8576
- async write(at, bytes) {
8577
- await this.fetch(this.pages.partial(at, bytes.length));
8578
- this.pages.write(at, bytes);
8579
- if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
8580
- }
8581
- truncate(size) {
8582
- this.pages.truncate(size);
8583
- }
8584
- async flush() {
8585
- if (this.pinned || !this.pages.changed) return;
8586
- const plan = this.pages.plan();
8587
- if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
8588
- for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
8589
- if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
8590
- this.version = void 0;
8591
- this.pages.flushed();
8592
- }
8593
- async pin() {
8594
- this.pinned = true;
8595
- this.pages.pin();
8596
- await this.fetch(this.pages.missing(0, this.pages.length));
8597
- }
8598
- };
8961
+ }
8962
+ function interruptible(dialing, signal) {
8963
+ return new Promise((resolve, reject) => {
8964
+ const abort = () => reject(new KernelError("EINTR"));
8965
+ signal.addEventListener("abort", abort, { once: true });
8966
+ dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
8967
+ });
8968
+ }
8969
+ function unnamed(domain) {
8970
+ return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
8971
+ }
8972
+ async function accept(proc, fd, nonblock) {
8973
+ const listener = socketAt(proc.fds, fd);
8974
+ const waits = !nonblock && !pollFile(listener).readable;
8975
+ const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
8976
+ return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
8977
+ }
8599
8978
 
8600
8979
  // src/kernel/vfs-file.ts
8601
8980
  var O_ACCMODE = 3;
@@ -9198,6 +9577,9 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
9198
9577
  "dl-closure",
9199
9578
  "dl-closure-free",
9200
9579
  "dl-reserve",
9580
+ "dl-load-reserve",
9581
+ "dl-lock",
9582
+ "dl-unlock",
9201
9583
  "fd-renumber",
9202
9584
  "fd-promote",
9203
9585
  "fd-open-tty",
@@ -9236,6 +9618,7 @@ function isWasmSyscall(req) {
9236
9618
  return typeof op === "string" && SYSCALL_OPS.has(op);
9237
9619
  }
9238
9620
  var MAX_READ = 1024 * 1024;
9621
+ var WRITE_ACCESS = 3;
9239
9622
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
9240
9623
  var DEFAULT_UMASK = 18;
9241
9624
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
@@ -9256,7 +9639,7 @@ var WasmProcess = class {
9256
9639
  stopped = 0;
9257
9640
  stops = 0;
9258
9641
  owed = false;
9259
- waits = 0;
9642
+ waiting = /* @__PURE__ */ new Set();
9260
9643
  resumed = Promise.resolve();
9261
9644
  wake;
9262
9645
  stateListeners = [];
@@ -9312,7 +9695,8 @@ var WasmProcess = class {
9312
9695
  return "terminate";
9313
9696
  }
9314
9697
  this.options.onPending?.(sig);
9315
- if (this.options.jobs?.keyboard && KEYBOARD.has(sig)) this.owed = true;
9698
+ if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
9699
+ this.owed = true;
9316
9700
  const blocked = this.interrupt;
9317
9701
  this.interrupt = new AbortController();
9318
9702
  blocked.abort();
@@ -9357,10 +9741,15 @@ var WasmProcess = class {
9357
9741
  await this.resumed;
9358
9742
  if (this.dying) return DYING;
9359
9743
  const stops = this.stops;
9360
- const waits = this.waits;
9361
- const result = await this.dispatch(req, cancelled);
9744
+ const scope = new AbortController();
9745
+ const forward = () => scope.abort();
9746
+ if (cancelled?.aborted) scope.abort();
9747
+ cancelled?.addEventListener("abort", forward, { once: true });
9748
+ const result = await this.dispatch(req, scope.signal).finally(() => {
9749
+ cancelled?.removeEventListener("abort", forward);
9750
+ this.waiting.delete(scope.signal);
9751
+ });
9362
9752
  if (this.dying) return DYING;
9363
- if (!result.ok && result.errno === "EINTR" && this.waits !== waits) this.owed = false;
9364
9753
  const restart = !result.ok && result.errno === "EINTR" && this.stops !== stops;
9365
9754
  if (restart && !this.options.hasPending?.()) continue;
9366
9755
  await this.resumed;
@@ -9413,12 +9802,31 @@ var WasmProcess = class {
9413
9802
  refuseIfPending(restart) {
9414
9803
  if (restart && this.options.hasPending?.()) throw new KernelError("EINTR");
9415
9804
  }
9805
+ async procWait(req, cancelled) {
9806
+ const flags = {
9807
+ untraced: req.untraced,
9808
+ continued: req.continued,
9809
+ inGroup: this.waitGroup(req.pid)
9810
+ };
9811
+ if (this.owed && !req.nohang) {
9812
+ const now = await this.children.wait(req.pid, true, this.interrupt.signal, flags);
9813
+ if (now[0] !== 0) return now;
9814
+ }
9815
+ const signal = req.nohang ? this.interrupt.signal : this.blockingSignal(cancelled);
9816
+ const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
9817
+ return waited;
9818
+ }
9416
9819
  blockingSignal(cancelled, owing = true) {
9417
- if (owing) this.waits++;
9418
- if (this.options.hasPending?.() || cancelled?.aborted || owing && this.owed) {
9820
+ if (owing && this.owed) {
9821
+ this.owed = false;
9419
9822
  throw new KernelError("EINTR");
9420
9823
  }
9421
- return cancelled ? AbortSignal.any([this.interrupt.signal, cancelled]) : this.interrupt.signal;
9824
+ if (this.options.hasPending?.() || cancelled.aborted) throw new KernelError("EINTR");
9825
+ if (owing) this.waiting.add(cancelled);
9826
+ return this.cancelSignal(cancelled);
9827
+ }
9828
+ cancelSignal(cancelled) {
9829
+ return AbortSignal.any([this.interrupt.signal, cancelled]);
9422
9830
  }
9423
9831
  async read(req, cancelled) {
9424
9832
  const file = this.fds.get(req.fd).file;
@@ -9442,15 +9850,22 @@ var WasmProcess = class {
9442
9850
  if (nonblock) return file.write(body, AbortSignal.abort());
9443
9851
  return file.write(
9444
9852
  body,
9445
- this.options.hasPending?.() ? AbortSignal.abort() : this.interrupt.signal
9853
+ this.options.hasPending?.() ? AbortSignal.abort() : this.cancelSignal(cancelled)
9446
9854
  );
9447
9855
  }
9448
9856
  async openVfs(req) {
9857
+ if (inProc(req.path)) return this.openProc(req, req.path);
9858
+ const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
9859
+ if (alias !== void 0) return this.openProc(req, alias);
9449
9860
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
9450
9861
  const fd = this.fds.install(heldFile(), 3);
9451
9862
  try {
9452
9863
  const nodes = this.nodes;
9453
9864
  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);
9865
+ if (inProc(path)) {
9866
+ this.fds.installAt(fd, await this.procFile(req, path));
9867
+ return fd;
9868
+ }
9454
9869
  const open = { ...req, path };
9455
9870
  if (req.exclusive) {
9456
9871
  await nodes.createExclusive(path);
@@ -9472,6 +9887,22 @@ var WasmProcess = class {
9472
9887
  }
9473
9888
  return fd;
9474
9889
  }
9890
+ async openProc(req, path) {
9891
+ return this.fds.install(await this.procFile(req, path), 3);
9892
+ }
9893
+ async procFile(req, path) {
9894
+ if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
9895
+ throw new KernelError("EROFS");
9896
+ }
9897
+ const tree = new ProcTree(
9898
+ {
9899
+ procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
9900
+ mountList: () => this.options.mounts?.() ?? []
9901
+ },
9902
+ (await this.options.identity?.())?.pid ?? this.pid
9903
+ );
9904
+ return memoryFile(tree.read(path), path, req.position);
9905
+ }
9475
9906
  openVfsFile(req) {
9476
9907
  return vfsFile(
9477
9908
  this.options.fs,
@@ -9563,6 +9994,11 @@ var WasmProcess = class {
9563
9994
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
9564
9995
  case "fd-select": {
9565
9996
  const { read, write, timeoutMs } = req;
9997
+ if (this.owed && timeoutMs !== 0) {
9998
+ const ready2 = await selectFds(this.fds, read, write, 0, this.interrupt.signal);
9999
+ if (ready2.read.length + ready2.write.length > 0)
10000
+ return { ok: true, kind: "json", json: ready2 };
10001
+ }
9566
10002
  const signal = this.blockingSignal(cancelled, timeoutMs !== 0);
9567
10003
  const selected = await selectFds(this.fds, read, write, timeoutMs, signal);
9568
10004
  return { ok: true, kind: "json", json: selected };
@@ -9760,16 +10196,8 @@ var WasmProcess = class {
9760
10196
  case "proc-spawn":
9761
10197
  this.refuseIfPending(req.restart);
9762
10198
  return { ok: true, kind: "json", json: await this.spawn(req) };
9763
- case "proc-wait": {
9764
- const signal = req.nohang ? this.interrupt.signal : this.blockingSignal(cancelled);
9765
- const flags = {
9766
- untraced: req.untraced,
9767
- continued: req.continued,
9768
- inGroup: this.waitGroup(req.pid)
9769
- };
9770
- const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
9771
- return { ok: true, kind: "json", json: waited };
9772
- }
10199
+ case "proc-wait":
10200
+ return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
9773
10201
  case "proc-identity":
9774
10202
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
9775
10203
  case "proc-exec": {
@@ -9801,7 +10229,7 @@ var WasmProcess = class {
9801
10229
  return { ok: true, kind: "json", json: this.options.mounts?.() ?? [] };
9802
10230
  case "mount":
9803
10231
  case "umount":
9804
- return this.mountSyscall(req);
10232
+ return this.mountSyscall(req, cancelled);
9805
10233
  case "proc-alarm":
9806
10234
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
9807
10235
  return { ok: true, kind: "void" };
@@ -9822,6 +10250,18 @@ var WasmProcess = class {
9822
10250
  return { ok: true, kind: "void" };
9823
10251
  case "dl-reserve":
9824
10252
  return { ok: true, kind: "json", json: this.reserveSlots(req.floor, req.size) };
10253
+ case "dl-load-reserve":
10254
+ return {
10255
+ ok: true,
10256
+ kind: "json",
10257
+ json: { base: this.reserveSlots(req.floor, req.size), handle: this.closures.handle++ }
10258
+ };
10259
+ case "dl-lock":
10260
+ await this.lockDl(req.owner);
10261
+ return { ok: true, kind: "void" };
10262
+ case "dl-unlock":
10263
+ this.unlockDl(req.owner);
10264
+ return { ok: true, kind: "void" };
9825
10265
  case "sig-mask":
9826
10266
  this.caught = req.caught;
9827
10267
  this.ignored = req.ignored;
@@ -9836,14 +10276,14 @@ var WasmProcess = class {
9836
10276
  return { ok: true, kind: "bytes", bytes: this.children.captured(req.pid, req.slot) };
9837
10277
  }
9838
10278
  }
9839
- async mountSyscall(req) {
10279
+ async mountSyscall(req, cancelled) {
9840
10280
  const { mount, umount } = this.options;
9841
10281
  if (!mount || !umount) throw new KernelError("ENOSYS");
9842
10282
  try {
9843
10283
  if (req.op === "umount") await umount(req.target, req.flags);
9844
10284
  else {
9845
10285
  const { source, target, type, flags, data } = req;
9846
- const signal = this.blockingSignal();
10286
+ const signal = this.blockingSignal(cancelled);
9847
10287
  await Promise.race([mount({ source, target, type, flags, data }, signal), aborted(signal)]);
9848
10288
  }
9849
10289
  } catch (err) {
@@ -9858,7 +10298,12 @@ var WasmProcess = class {
9858
10298
  }
9859
10299
  alarm;
9860
10300
  dlLog = [];
9861
- closures = { next: 0, free: [], live: /* @__PURE__ */ new Set() };
10301
+ closures = {
10302
+ next: 0,
10303
+ handle: 1,
10304
+ free: [],
10305
+ live: /* @__PURE__ */ new Set()
10306
+ };
9862
10307
  allocateClosure(floor) {
9863
10308
  const index = this.closures.free.pop() ?? Math.max(floor, this.closures.next);
9864
10309
  this.closures.next = Math.max(this.closures.next, index + 1);
@@ -9871,6 +10316,47 @@ var WasmProcess = class {
9871
10316
  this.closures.free.push(index);
9872
10317
  this.dlLog.push({ kind: "closure-free", index });
9873
10318
  }
10319
+ dlLock = {
10320
+ owner: 0,
10321
+ depth: 0,
10322
+ waiters: /* @__PURE__ */ new Set()
10323
+ };
10324
+ async lockDl(owner) {
10325
+ while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
10326
+ if (this.dying) throw new KernelError("EINTR");
10327
+ const interrupt = this.interrupt.signal;
10328
+ const gone = await new Promise((resolve) => {
10329
+ const waiter = {
10330
+ owner,
10331
+ wake: (ended) => {
10332
+ this.dlLock.waiters.delete(waiter);
10333
+ interrupt.removeEventListener("abort", recheck);
10334
+ resolve(ended);
10335
+ }
10336
+ };
10337
+ const recheck = () => waiter.wake(false);
10338
+ this.dlLock.waiters.add(waiter);
10339
+ interrupt.addEventListener("abort", recheck, { once: true });
10340
+ });
10341
+ if (gone) throw new KernelError("EINTR");
10342
+ }
10343
+ this.dlLock.owner = owner;
10344
+ this.dlLock.depth++;
10345
+ }
10346
+ threadEnded(tid) {
10347
+ for (const waiter of [...this.dlLock.waiters]) if (waiter.owner === tid) waiter.wake(true);
10348
+ if (this.dlLock.depth === 0 || this.dlLock.owner !== tid) return;
10349
+ this.dlLock.depth = 0;
10350
+ this.wakeDl();
10351
+ }
10352
+ unlockDl(owner) {
10353
+ if (this.dlLock.depth === 0 || this.dlLock.owner !== owner) throw new KernelError("EPERM");
10354
+ this.dlLock.depth--;
10355
+ if (this.dlLock.depth === 0) this.wakeDl();
10356
+ }
10357
+ wakeDl() {
10358
+ for (const waiter of [...this.dlLock.waiters]) waiter.wake(false);
10359
+ }
9874
10360
  reserveSlots(floor, size) {
9875
10361
  const base = Math.max(floor, this.closures.next);
9876
10362
  this.closures.next = base + size;
@@ -10182,6 +10668,7 @@ function spawnWasmProcess(opts) {
10182
10668
  const endThread = (tid) => {
10183
10669
  const end = threads.get(tid);
10184
10670
  threads.delete(tid);
10671
+ process2.threadEnded(tid);
10185
10672
  end?.();
10186
10673
  };
10187
10674
  const spawnThread = (thread) => {
@@ -10974,8 +11461,8 @@ function umountCall(target, flags) {
10974
11461
  var FSTAB_PATH = "/etc/fstab";
10975
11462
  var FSTAB_RETRIES = [1e3, 4e3, 16e3];
10976
11463
  var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
10977
- function unescape(field2) {
10978
- return field2.replace(
11464
+ function unescape(field3) {
11465
+ return field3.replace(
10979
11466
  /\\([0-7]{3})/g,
10980
11467
  (_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
10981
11468
  );
@@ -11868,7 +12355,7 @@ function importedMemory(bytes) {
11868
12355
  const count = u32();
11869
12356
  for (let i = 0; i < count; i++) {
11870
12357
  const module = name2();
11871
- const field2 = name2();
12358
+ const field3 = name2();
11872
12359
  const kind = bytes[at++];
11873
12360
  if (kind === 0) u32();
11874
12361
  else if (kind === 1) {
@@ -11876,7 +12363,7 @@ function importedMemory(bytes) {
11876
12363
  limits();
11877
12364
  } else if (kind === 2) {
11878
12365
  const l = limits();
11879
- return { module, name: field2, ...l };
12366
+ return { module, name: field3, ...l };
11880
12367
  } else if (kind === 3) at += 2;
11881
12368
  else if (kind === 4) {
11882
12369
  at++;
@@ -11967,12 +12454,12 @@ function readImports(r, types) {
11967
12454
  const foreign = {};
11968
12455
  for (let i = r.u32(); i > 0; i--) {
11969
12456
  const module = r.name();
11970
- const field2 = r.name();
12457
+ const field3 = r.name();
11971
12458
  const kind = r.byte();
11972
12459
  if (kind !== 0) skipImport(r, kind);
11973
12460
  else {
11974
12461
  const type = types[r.u32()] ?? "other";
11975
- if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field2] = type;
12462
+ if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field3] = type;
11976
12463
  }
11977
12464
  }
11978
12465
  return foreign;