@ai-ecoverse/slicc-kernel 1.66.1 → 1.67.1

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
@@ -5535,11 +5535,11 @@ var ChildTable = class {
5535
5535
  return [...this.children.keys()];
5536
5536
  }
5537
5537
  captured(pid, slot2) {
5538
- const slots = this.leftovers.get(pid);
5539
- const chunks2 = slots?.get(slot2);
5540
- if (!slots || !chunks2) return new Uint8Array(0);
5541
- slots.delete(slot2);
5542
- if (slots.size === 0) this.leftovers.delete(pid);
5538
+ const slots2 = this.leftovers.get(pid);
5539
+ const chunks2 = slots2?.get(slot2);
5540
+ if (!slots2 || !chunks2) return new Uint8Array(0);
5541
+ slots2.delete(slot2);
5542
+ if (slots2.size === 0) this.leftovers.delete(pid);
5543
5543
  return concat2(chunks2);
5544
5544
  }
5545
5545
  };
@@ -7011,7 +7011,9 @@ async function dispatchOne(entry, req) {
7011
7011
 
7012
7012
  // src/realm/sync-sab-wire.ts
7013
7013
  var SAB_HEADER_I32 = 16;
7014
- var SAB_HEADER_BYTES = SAB_HEADER_I32 * 4;
7014
+ var SAB_CLOCK_OFFSET = SAB_HEADER_I32 * 4;
7015
+ var SAB_CLOCK_I64 = 3;
7016
+ var SAB_HEADER_BYTES = SAB_CLOCK_OFFSET + SAB_CLOCK_I64 * 8;
7015
7017
  var SAB_I_STATE = 0;
7016
7018
  var SAB_I_SEQ = 1;
7017
7019
  var SAB_I_STATUS = 2;
@@ -7161,6 +7163,146 @@ function attachSyncSabResponder(port, sab, token, opts) {
7161
7163
  };
7162
7164
  }
7163
7165
 
7166
+ // src/kernel/cpu-clock.ts
7167
+ var IDLE_OPS = /* @__PURE__ */ new Set([
7168
+ "fd-select",
7169
+ "proc-wait",
7170
+ "proc-exec",
7171
+ "sig-pause",
7172
+ "sock-accept",
7173
+ "sock-connect"
7174
+ ]);
7175
+ var SYS_CAP_US = 2e4;
7176
+ var LOCAL = SAB_CLOCK_OFFSET;
7177
+ var IDLE = 1;
7178
+ function epochMicros() {
7179
+ return Math.floor((performance.timeOrigin + performance.now()) * 1e3);
7180
+ }
7181
+ function slots(sab) {
7182
+ return new BigInt64Array(sab, LOCAL, SAB_CLOCK_I64);
7183
+ }
7184
+ var rawWait = Atomics.wait.bind(Atomics);
7185
+ function localIdle(sab, now) {
7186
+ const state = Number(Atomics.load(slots(sab), IDLE));
7187
+ if (state >= 0) return { idle: state, waiting: false };
7188
+ return { idle: Math.max(0, now + state), waiting: true };
7189
+ }
7190
+ var CpuClock = class {
7191
+ start;
7192
+ sys = 0;
7193
+ idle = 0;
7194
+ since = 0;
7195
+ kind = "sys";
7196
+ stopped = false;
7197
+ last = { utime: 0, stime: 0 };
7198
+ sab;
7199
+ now;
7200
+ constructor(sab, now = epochMicros) {
7201
+ this.sab = sab;
7202
+ this.now = now;
7203
+ this.start = now();
7204
+ }
7205
+ enter(op) {
7206
+ this.since = this.now();
7207
+ this.kind = this.stopped || IDLE_OPS.has(op) ? "idle" : "sys";
7208
+ }
7209
+ leave() {
7210
+ if (this.since === 0) return;
7211
+ this.add((this.frozen ?? this.now()) - this.since);
7212
+ this.since = 0;
7213
+ }
7214
+ stop(stopped) {
7215
+ if (stopped === this.stopped) return;
7216
+ this.stopped = stopped;
7217
+ if (this.since === 0 || !stopped || this.frozen !== void 0) return;
7218
+ const now = this.now();
7219
+ this.add(now - this.since);
7220
+ this.since = now;
7221
+ this.kind = "idle";
7222
+ }
7223
+ frozen;
7224
+ freeze() {
7225
+ this.frozen ??= this.now();
7226
+ }
7227
+ running() {
7228
+ if (this.frozen !== void 0) return false;
7229
+ const now = this.now();
7230
+ const busy = this.since === 0 || this.kind === "sys" && now - this.since < SYS_CAP_US;
7231
+ return busy && !localIdle(this.sab, now).waiting;
7232
+ }
7233
+ times() {
7234
+ const now = this.frozen ?? this.now();
7235
+ const open = this.since === 0 ? 0 : Math.max(0, now - this.since);
7236
+ const busy = this.kind === "sys" ? Math.min(open, SYS_CAP_US) : 0;
7237
+ const sys = this.sys + busy;
7238
+ const idle = this.idle + (open - busy) + localIdle(this.sab, now).idle;
7239
+ const user = Math.max(0, now - this.start - sys - idle);
7240
+ this.last = {
7241
+ utime: Math.max(this.last.utime, Math.floor(user / 1e3)),
7242
+ stime: Math.max(this.last.stime, Math.floor(sys / 1e3))
7243
+ };
7244
+ return this.last;
7245
+ }
7246
+ add(micros) {
7247
+ const spent = Math.max(0, micros);
7248
+ const busy = this.kind === "sys" ? Math.min(spent, SYS_CAP_US) : 0;
7249
+ this.sys += busy;
7250
+ this.idle += spent - busy;
7251
+ }
7252
+ };
7253
+ var ProcessClocks = class {
7254
+ main;
7255
+ threads = /* @__PURE__ */ new Map();
7256
+ ended = { utime: 0, stime: 0 };
7257
+ enabled;
7258
+ constructor(sab, enabled = true, states) {
7259
+ this.main = new CpuClock(sab);
7260
+ this.enabled = enabled;
7261
+ states?.onState((state) => this.stop(state === "stopped"));
7262
+ }
7263
+ thread(tid, sab) {
7264
+ const clock = new CpuClock(sab);
7265
+ this.threads.set(tid, clock);
7266
+ return clock;
7267
+ }
7268
+ threadEnded(tid) {
7269
+ const spent = this.threads.get(tid)?.times();
7270
+ this.threads.delete(tid);
7271
+ if (!spent) return;
7272
+ this.ended.utime += spent.utime;
7273
+ this.ended.stime += spent.stime;
7274
+ }
7275
+ stop(stopped) {
7276
+ for (const c of this.all()) c.stop(stopped);
7277
+ }
7278
+ freeze() {
7279
+ for (const c of this.all()) c.freeze();
7280
+ }
7281
+ times() {
7282
+ if (!this.enabled) return void 0;
7283
+ const sum = { ...this.ended };
7284
+ for (const c of this.all()) {
7285
+ const t = c.times();
7286
+ sum.utime += t.utime;
7287
+ sum.stime += t.stime;
7288
+ }
7289
+ return sum;
7290
+ }
7291
+ running() {
7292
+ return this.enabled ? this.all().filter((c) => c.running()).length : 0;
7293
+ }
7294
+ timed(clock, dispatch2) {
7295
+ const c = clock ?? this.main;
7296
+ return (req) => {
7297
+ c.enter(req.op);
7298
+ return dispatch2(req).finally(() => c.leave());
7299
+ };
7300
+ }
7301
+ all() {
7302
+ return [this.main, ...this.threads.values()];
7303
+ }
7304
+ };
7305
+
7164
7306
  // src/kernel/net/loopback-names.ts
7165
7307
  var HOST_LOOPBACK = "host.slicc.internal";
7166
7308
  var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
@@ -7472,8 +7614,8 @@ function pidStat(info, boot) {
7472
7614
  0,
7473
7615
  0,
7474
7616
  0,
7475
- 0,
7476
- 0,
7617
+ ticks(info.utime ?? 0),
7618
+ ticks(info.stime ?? 0),
7477
7619
  0,
7478
7620
  0,
7479
7621
  20,
@@ -7567,19 +7709,23 @@ function systemFile(name2, listing, now, memory) {
7567
7709
  if (name2 === "meminfo") {
7568
7710
  return memoryInfo(
7569
7711
  memory,
7570
- listing.processes.reduce((sum, p) => sum + p.memory, 0)
7712
+ listing.processes.reduce((sum2, p) => sum2 + p.memory, 0)
7571
7713
  );
7572
7714
  }
7573
7715
  const live = listing.processes.filter((p) => p.state !== "Z");
7574
7716
  if (name2 === "loadavg") {
7575
7717
  const last = Math.max(0, ...listing.processes.map((p) => p.pid));
7576
- return `0.00 0.00 0.00 1/${live.length} ${last}
7718
+ const [one, five, fifteen] = (listing.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
7719
+ return `${one} ${five} ${fifteen} 1/${live.length} ${last}
7577
7720
  `;
7578
7721
  }
7579
- const idle = ticks(now - listing.boot);
7722
+ const sum = (key) => listing.cpu?.[key] ?? listing.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
7723
+ const user = ticks(sum("utime"));
7724
+ const system = ticks(sum("stime"));
7725
+ const idle = listing.cpu ? ticks(listing.cpu.idle) : Math.max(0, ticks(now - listing.boot) - user - system);
7580
7726
  return [
7581
- `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
7582
- `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
7727
+ `cpu ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
7728
+ `cpu0 ${user} 0 ${system} ${idle} 0 0 0 0 0 0`,
7583
7729
  "intr 0",
7584
7730
  "ctxt 0",
7585
7731
  `btime ${Math.floor(listing.boot / 1e3)}`,
@@ -10172,8 +10318,8 @@ function ptySyscall(req, ctx) {
10172
10318
  var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10173
10319
  var RUN_MAX_BUFFER = 1024 * 1024;
10174
10320
  function capLimit(maxBuffer) {
10175
- const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10176
- return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10321
+ const asked = typeof maxBuffer === "number" && maxBuffer >= 0 ? maxBuffer : RUN_MAX_BUFFER;
10322
+ return Math.min(asked, RUN_CAPTURE_CAP);
10177
10323
  }
10178
10324
  function slot(how, n, extra) {
10179
10325
  if (how === "inherit") return { fd: n };
@@ -10199,6 +10345,7 @@ async function reaped(deps, pid) {
10199
10345
  }
10200
10346
  }
10201
10347
  async function runChild(req, deps) {
10348
+ if (!isSignal(req.killSignal)) throw new KernelError("EINVAL");
10202
10349
  const max = capLimit(req.maxBuffer);
10203
10350
  let failure;
10204
10351
  let pid = 0;
@@ -12039,6 +12186,9 @@ function killThread(thread, caught, sig) {
12039
12186
  blocked.abort();
12040
12187
  return true;
12041
12188
  }
12189
+ function threadSignalsOf(sab) {
12190
+ return { header: new Int32Array(sab, 0, SAB_HEADER_I32), interrupt: new AbortController() };
12191
+ }
12042
12192
  function threadCancel(thread) {
12043
12193
  return Atomics.load(thread.header, SAB_I_SIGNALS) !== 0 ? AbortSignal.abort() : thread.interrupt.signal;
12044
12194
  }
@@ -12164,6 +12314,22 @@ function processOptions(opts, header4, hooks, created3) {
12164
12314
  ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {}
12165
12315
  };
12166
12316
  }
12317
+ function threadInit(opts, program, sab, cred, thread) {
12318
+ return {
12319
+ type: WASM_THREAD_INIT,
12320
+ pid: opts.shownPid ?? opts.pid,
12321
+ program,
12322
+ argv0: opts.argv0,
12323
+ args: opts.args,
12324
+ env: opts.env,
12325
+ cwd: opts.cwd,
12326
+ sab,
12327
+ cred: copyCred(cred),
12328
+ owners: opts.owners === true,
12329
+ ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
12330
+ thread
12331
+ };
12332
+ }
12167
12333
  function spawnWasmProcess(opts) {
12168
12334
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
12169
12335
  const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
@@ -12196,7 +12362,10 @@ function spawnWasmProcess(opts) {
12196
12362
  const worker = opts.createWorker();
12197
12363
  const dying = new Dying();
12198
12364
  const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ? process2.syscall(req) : dispatchSyncFs(req));
12199
- const responder = attachSyncSabResponder(worker, sab, token, { dispatch: dispatch2 });
12365
+ const clocks = new ProcessClocks(sab, opts.program.abi !== "js", process2);
12366
+ const responder = attachSyncSabResponder(worker, sab, token, {
12367
+ dispatch: clocks.timed(void 0, dispatch2)
12368
+ });
12200
12369
  const lane = opts.program.abi === "js" ? openLane(token, dispatch2) : void 0;
12201
12370
  const { promise: exited, resolve: settle } = Promise.withResolvers();
12202
12371
  let done4 = false;
@@ -12204,6 +12373,7 @@ function spawnWasmProcess(opts) {
12204
12373
  const finish = (code, sig) => {
12205
12374
  if (done4) return;
12206
12375
  done4 = true;
12376
+ clocks.freeze();
12207
12377
  dying.clear();
12208
12378
  endedBy = sig ?? process2.execTermsig;
12209
12379
  worker.removeEventListener("message", onMessage);
@@ -12244,6 +12414,7 @@ function spawnWasmProcess(opts) {
12244
12414
  const endThread = (tid) => {
12245
12415
  const end = threads.get(tid);
12246
12416
  threads.delete(tid);
12417
+ clocks.threadEnded(tid);
12247
12418
  process2.threadEnded(tid);
12248
12419
  end?.();
12249
12420
  };
@@ -12257,15 +12428,14 @@ function spawnWasmProcess(opts) {
12257
12428
  }
12258
12429
  const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
12259
12430
  const tw = opts.createWorker();
12260
- const tsignals = {
12261
- header: new Int32Array(tsab, 0, SAB_HEADER_I32),
12262
- interrupt: new AbortController()
12263
- };
12431
+ const tsignals = threadSignalsOf(tsab);
12264
12432
  threadSignals.set(thread.tid, tsignals);
12265
12433
  const tdispatch = (req) => dying.serving(
12266
12434
  () => isWasmSyscall(req) ? process2.syscall(req, threadCancel(tsignals)) : dispatchSyncFs(req)
12267
12435
  );
12268
- const tresponder = attachSyncSabResponder(tw, tsab, token, { dispatch: tdispatch });
12436
+ const tresponder = attachSyncSabResponder(tw, tsab, token, {
12437
+ dispatch: clocks.timed(clocks.thread(thread.tid, tsab), tdispatch)
12438
+ });
12269
12439
  const onThreadMessage = (event) => {
12270
12440
  const data = event.data;
12271
12441
  if (data?.type === WASM_THREAD_EXIT) endThread(thread.tid);
@@ -12280,21 +12450,7 @@ function spawnWasmProcess(opts) {
12280
12450
  tresponder.dispose();
12281
12451
  tw.terminate();
12282
12452
  });
12283
- const tinit = {
12284
- type: WASM_THREAD_INIT,
12285
- pid: opts.shownPid ?? opts.pid,
12286
- program,
12287
- argv0: opts.argv0,
12288
- args: opts.args,
12289
- env: opts.env,
12290
- cwd: opts.cwd,
12291
- sab: tsab,
12292
- cred: copyCred(process2.cred),
12293
- owners: opts.owners === true,
12294
- ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
12295
- thread
12296
- };
12297
- tw.postMessage(tinit);
12453
+ tw.postMessage(threadInit(opts, program, tsab, process2.cred, thread));
12298
12454
  };
12299
12455
  worker.postMessage(
12300
12456
  processInit(opts, sab, process2.umask, copyCred(process2.cred), lane?.lane),
@@ -12319,7 +12475,9 @@ function spawnWasmProcess(opts) {
12319
12475
  signalMasks: () => process2.signalMasks(),
12320
12476
  umask: () => process2.umask,
12321
12477
  cred: () => copyCred(process2.cred),
12322
- writesBack: () => opts.program.abi === "wasi" && !done4
12478
+ writesBack: () => opts.program.abi === "wasi" && !done4,
12479
+ times: () => clocks.times(),
12480
+ running: () => clocks.running()
12323
12481
  };
12324
12482
  }
12325
12483
 
@@ -14738,6 +14896,8 @@ var LAYOUT = [
14738
14896
  ["/usr", 493],
14739
14897
  ["/usr/local", 493]
14740
14898
  ];
14899
+ var LOAD_SAMPLE_MS = 5e3;
14900
+ var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
14741
14901
  var encoder3 = new TextEncoder();
14742
14902
  var USERS_MODES = ["off", "report", "enforce"];
14743
14903
  function usersMode(mode = "off") {
@@ -15266,6 +15426,7 @@ var Launcher = class {
15266
15426
  return this.jobs.sessionOf(pgid);
15267
15427
  }
15268
15428
  start(req) {
15429
+ this.sampling();
15269
15430
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
15270
15431
  throw fsError("ESRCH", `no process group ${req.pgid}`);
15271
15432
  }
@@ -15306,7 +15467,12 @@ var Launcher = class {
15306
15467
  forker: this.forker(pid, req),
15307
15468
  kill: (target, sig) => this.kill(target, sig, this.credOf(pid), pid),
15308
15469
  writesBack: (target) => this.processes.get(target)?.writesBack() === true,
15309
- processes: () => ({ boot: this.boot, processes: this.list(this.credOf(pid)) }),
15470
+ processes: () => ({
15471
+ boot: this.boot,
15472
+ processes: this.list(this.credOf(pid)),
15473
+ load: this.loadAverage(),
15474
+ cpu: this.cpuTotal()
15475
+ }),
15310
15476
  sudo: (call, signal) => this.sudo(pid, call, signal),
15311
15477
  openFiles: this.openFiles,
15312
15478
  nodes: this.nodes,
@@ -15340,7 +15506,14 @@ var Launcher = class {
15340
15506
  started: Date.now()
15341
15507
  });
15342
15508
  void handle.exited.then(() => {
15509
+ const spent = handle.times?.();
15510
+ if (spent) this.spent.set(pid, spent);
15343
15511
  this.processes.delete(pid);
15512
+ if (this.processes.size === 0) {
15513
+ clearInterval(this.sampler);
15514
+ this.sampler = void 0;
15515
+ this.idleSince = Date.now();
15516
+ }
15344
15517
  this.settling.settled(pid);
15345
15518
  if (req.ppid === void 0 || this.orphans.delete(pid)) this.forget(pid);
15346
15519
  else this.zombies.add(pid);
@@ -15356,6 +15529,12 @@ var Launcher = class {
15356
15529
  forget(pid) {
15357
15530
  this.jobs.remove(pid);
15358
15531
  this.described.delete(pid);
15532
+ const spent = this.spent.get(pid);
15533
+ if (spent) {
15534
+ this.reapedTimes.utime += spent.utime;
15535
+ this.reapedTimes.stime += spent.stime;
15536
+ }
15537
+ this.spent.delete(pid);
15359
15538
  this.creds.delete(pid);
15360
15539
  this.tags.delete(pid);
15361
15540
  this.granted.delete(pid);
@@ -15364,6 +15543,70 @@ var Launcher = class {
15364
15543
  if (this.zombies.delete(pid)) this.forget(pid);
15365
15544
  else this.orphans.add(pid);
15366
15545
  }
15546
+ load = [0, 0, 0];
15547
+ sampler;
15548
+ reapedTimes = { utime: 0, stime: 0 };
15549
+ idleAt = { wall: this.boot, busy: 0, idle: 0 };
15550
+ cpuTotal(now = Date.now()) {
15551
+ const total = { ...this.reapedTimes };
15552
+ const add = (t) => {
15553
+ total.utime += t?.utime ?? 0;
15554
+ total.stime += t?.stime ?? 0;
15555
+ };
15556
+ for (const handle of this.processes.values()) add(handle.times?.());
15557
+ for (const t of this.spent.values()) add(t);
15558
+ const busy = total.utime + total.stime;
15559
+ const at = this.idleAt;
15560
+ at.idle += Math.max(0, now - at.wall - (busy - at.busy));
15561
+ at.wall = Math.max(at.wall, now);
15562
+ at.busy = Math.max(at.busy, busy);
15563
+ return { ...total, idle: at.idle };
15564
+ }
15565
+ loadAverage() {
15566
+ return this.load;
15567
+ }
15568
+ sampledAt = Date.now();
15569
+ sampleLoad(now = Date.now()) {
15570
+ let running = 0;
15571
+ for (const handle of this.processes.values()) running += handle.running?.() ?? 0;
15572
+ const elapsed = Math.max(LOAD_SAMPLE_MS, now - this.sampledAt);
15573
+ this.sampledAt = Math.max(this.sampledAt, now);
15574
+ this.load = this.load.map((avg, i) => {
15575
+ const window = LOAD_WINDOWS_MS[i];
15576
+ const fresh2 = 1 - Math.exp(-LOAD_SAMPLE_MS / window);
15577
+ return avg * Math.exp(-elapsed / window) + running * fresh2;
15578
+ });
15579
+ }
15580
+ idleSince;
15581
+ sampling() {
15582
+ if (this.sampler) return;
15583
+ if (this.idleSince !== void 0) {
15584
+ const empty = Math.max(0, Date.now() - this.idleSince);
15585
+ this.load = this.load.map(
15586
+ (avg, i) => avg * Math.exp(-empty / LOAD_WINDOWS_MS[i])
15587
+ );
15588
+ }
15589
+ this.sampledAt = Date.now();
15590
+ this.sampler = setInterval(() => this.sampleLoad(), LOAD_SAMPLE_MS);
15591
+ this.sampler.unref?.();
15592
+ }
15593
+ spent = /* @__PURE__ */ new Map();
15594
+ timesAt(pid) {
15595
+ return this.processes.get(pid)?.times?.() ?? this.spent.get(pid);
15596
+ }
15597
+ timesOf(member, members) {
15598
+ let utime = 0;
15599
+ let stime = 0;
15600
+ let timed2 = false;
15601
+ for (let at = member; at; at = members.get(at.execParent ?? -1)) {
15602
+ const t = this.timesAt(at.pid);
15603
+ if (!t) continue;
15604
+ timed2 = true;
15605
+ utime += t.utime;
15606
+ stime += t.stime;
15607
+ }
15608
+ return timed2 ? { utime, stime } : {};
15609
+ }
15367
15610
  list(viewer = ROOT) {
15368
15611
  const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
15369
15612
  const rootOf = (member) => {
@@ -15395,6 +15638,7 @@ var Launcher = class {
15395
15638
  started: (this.described.get(root.pid) ?? described2).started,
15396
15639
  state: this.processes.has(member.pid) ? "S" : "Z",
15397
15640
  memory: this.processes.get(member.pid)?.memory() ?? 0,
15641
+ ...this.timesOf(member, members),
15398
15642
  umask: this.umaskOf(member.pid),
15399
15643
  cred,
15400
15644
  ...this.signalMasksOf(member.pid)