@ai-ecoverse/slicc-kernel 1.33.0 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,7 +65,7 @@ Starts `argv` as the session leader on a new terminal and resolves once it is ru
65
65
  | --- | --- |
66
66
  | `pid` | the session leader's pid |
67
67
  | `onData` | the output listener; output that arrives while it is `null` is kept and delivered when one is set |
68
- | `write(data)` | types a string or bytes into the terminal; with `ISIG` on, `^C`, `^Z` and `^\` signal the foreground process group. A key typed while a child of that group is still setting up is held until the child has finished, then sent to the group in the foreground at that point. Setting up means the child's calls before its first other one: `setpgid`, `tcsetpgrp` and signal masks. The hold ends 250 ms after the child's first call at the latest. So `^Z` right after a command line stops the new job, as it does on Linux, where that setup takes microseconds rather than a worker start. |
68
+ | `write(data)` | types a string or bytes into the terminal; with `ISIG` on, `^C`, `^Z` and `^\` signal the foreground process group. A key typed while a child of that group is still setting up is held until the child has finished, then sent to the group in the foreground at that point. Setting up means the child's calls before its first other one: `setpgid`, `tcsetpgrp`, signal masks, and starting the program it execs (which then sets itself up too). The hold ends 2 s after the child's first call at the latest. A key typed while the foreground group is busy, not reading the terminal (a shell between reading a command line and starting it), goes to that group and also to the job the group starts next, once that job has taken the terminal; the second copy is dropped when the group reads the terminal again. So `^C` or `^Z` right after a command line reaches the new job, as it does on Linux, where starting a job takes microseconds rather than a worker start. |
69
69
  | `resize(cols, rows)` | sets the window size (`TIOCGWINSZ`) and sends `SIGWINCH` to the foreground process group |
70
70
  | `signal(name)` | sends a signal (`'SIGINT'`, `'SIGTSTP'`, `'SIGQUIT'`, `'SIGHUP'`, …) to the foreground process group, regardless of termios |
71
71
  | `exited` | resolves with the session leader's exit status (`128 + n` if killed by signal `n`) |
@@ -334,6 +334,8 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
334
334
 
335
335
  Directories are renamed with `FileSystemHandle.move()` where available, else by copy and delete.
336
336
 
337
+ `mmap` of a regular file works on every file system, for Emscripten programs built with `mmap` (whose glue exports `emscripten_builtin_memalign`) as well as for WASIX ones: the kernel reads the mapped range into fresh memory of the process, as Emscripten's MEMFS does. A `MAP_PRIVATE` mapping may be written, and its changes stay in the process. A writable `MAP_SHARED` mapping of such a file is `ENODEV`; a file in the process's own in-memory file system (MEMFS, as under `/dev`) keeps Emscripten's own mapping, which writes back. A read-only `MAP_SHARED` mapping is a copy taken at `mmap`, so later changes to the file do not show in it.
338
+
337
339
  ### Mounts
338
340
 
339
341
  `kernel.mount({ type, source, target, options })` mounts a file system on an existing directory. Every process sees it, Emscripten and WASI alike, and so does every attached client. `kernel.umount(target)` unmounts it, and fails with `EBUSY` while a process has a file open under it. `kernel.mounts()` lists the table, as does `/proc/mounts`. The Node entry and attached clients have the same three calls (client protocol 1.2).
@@ -6568,6 +6568,8 @@ var KernelTty = class {
6568
6568
  waiters = [];
6569
6569
  hungUp = false;
6570
6570
  name;
6571
+ onRead;
6572
+ readers = 0;
6571
6573
  screen;
6572
6574
  signal;
6573
6575
  constructor(screen, signal) {
@@ -6718,7 +6720,19 @@ var KernelTty = class {
6718
6720
  }
6719
6721
  this.screen.write(Uint8Array.from(out));
6720
6722
  }
6723
+ get reading() {
6724
+ return this.readers > 0;
6725
+ }
6721
6726
  async read(max, signal) {
6727
+ this.onRead?.();
6728
+ this.readers++;
6729
+ try {
6730
+ return await this.take(max, signal);
6731
+ } finally {
6732
+ this.readers--;
6733
+ }
6734
+ }
6735
+ async take(max, signal) {
6722
6736
  while (this.readable.length === 0) {
6723
6737
  if (this.hungUp) return new Uint8Array(0);
6724
6738
  await this.changed(signal);
@@ -6783,13 +6797,14 @@ var PtyPair = class {
6783
6797
  slaveSeen = false;
6784
6798
  index;
6785
6799
  onFree;
6786
- constructor(index, signal, onFree) {
6800
+ constructor(index, signal, onFree, onRead) {
6787
6801
  this.index = index;
6788
6802
  this.onFree = onFree;
6789
6803
  this.slave = new KernelTty(
6790
6804
  { write: (bytes) => this.fromSlave(bytes) },
6791
6805
  (sig) => signal(this.slave, sig)
6792
6806
  );
6807
+ if (onRead) this.slave.onRead = () => onRead(this.slave);
6793
6808
  this.slave.name = `/dev/pts/${index}`;
6794
6809
  }
6795
6810
  master() {
@@ -6887,13 +6902,15 @@ var PtyPair = class {
6887
6902
  var PtyTable = class {
6888
6903
  pairs = /* @__PURE__ */ new Map();
6889
6904
  signal;
6890
- constructor(signal) {
6905
+ onRead;
6906
+ constructor(signal, onRead) {
6891
6907
  this.signal = signal;
6908
+ this.onRead = onRead;
6892
6909
  }
6893
6910
  open() {
6894
6911
  let index = 0;
6895
6912
  while (this.pairs.has(index)) index += 1;
6896
- const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index));
6913
+ const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index), this.onRead);
6897
6914
  this.pairs.set(index, pair);
6898
6915
  return { pair, master: pair.master() };
6899
6916
  }
@@ -8446,6 +8463,7 @@ var WasmProcess = class {
8446
8463
  async exit() {
8447
8464
  if (this.exited) return;
8448
8465
  this.exited = true;
8466
+ this.die();
8449
8467
  this.clearAlarm();
8450
8468
  this.interrupt.abort();
8451
8469
  this.options.locks?.release(this.pid);
@@ -8858,13 +8876,16 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
8858
8876
  "fd-info",
8859
8877
  "sig-mask",
8860
8878
  "proc-setpgid",
8861
- "tty-pgrp-set"
8879
+ "tty-pgrp-set",
8880
+ "proc-exec",
8881
+ "proc-spawn"
8862
8882
  ]);
8863
- var SETTLE_MS = 250;
8883
+ var SETTLE_MS = 2e3;
8864
8884
  var KEY_SIGNALS = /* @__PURE__ */ new Set([2, 3, 20]);
8865
8885
  var SettlingChildren = class {
8866
8886
  children = /* @__PURE__ */ new Map();
8867
8887
  held = [];
8888
+ parked = [];
8868
8889
  pgidOf;
8869
8890
  settleMs;
8870
8891
  constructor(pgidOf, settleMs = SETTLE_MS) {
@@ -8872,7 +8893,15 @@ var SettlingChildren = class {
8872
8893
  this.settleMs = settleMs;
8873
8894
  }
8874
8895
  started(pid, ppid) {
8875
- this.children.set(pid, { ppid });
8896
+ const parent = this.children.get(ppid);
8897
+ this.children.set(pid, parent ? { ppid: parent.ppid, execer: ppid } : { ppid });
8898
+ const group = this.pgidOf(ppid);
8899
+ for (const p of this.parked) if (p.pgid === group) this.held.push({ ...p, copy: true });
8900
+ this.parked = this.parked.filter((p) => p.pgid !== group);
8901
+ }
8902
+ reading(pgid) {
8903
+ this.parked = this.parked.filter((p) => p.pgid !== pgid);
8904
+ this.held = this.held.filter((h) => !(h.copy && h.pgid === pgid));
8876
8905
  }
8877
8906
  syscall(pid, op) {
8878
8907
  const child = this.children.get(pid);
@@ -8888,13 +8917,25 @@ var SettlingChildren = class {
8888
8917
  if (!child) return;
8889
8918
  clearTimeout(child.timer);
8890
8919
  this.children.delete(pid);
8920
+ if (child.execer !== void 0) this.settled(child.execer);
8891
8921
  const ready2 = this.held.filter((h) => !this.blocking(h.pgid));
8892
8922
  this.held = this.held.filter((h) => this.blocking(h.pgid));
8893
8923
  for (const h of ready2) h.send();
8894
8924
  }
8895
- deliver(sig, pgid, send) {
8896
- if (KEY_SIGNALS.has(sig) && this.blocking(pgid)) this.held.push({ pgid, send });
8897
- else send();
8925
+ deliver(sig, pgid, send, foreground) {
8926
+ if (!KEY_SIGNALS.has(sig)) {
8927
+ send();
8928
+ } else if (this.blocking(pgid)) {
8929
+ this.held.push({ pgid, send });
8930
+ } else if (foreground) {
8931
+ send();
8932
+ const moved2 = () => {
8933
+ if (foreground() !== pgid) send();
8934
+ };
8935
+ this.parked.push({ pgid, send: moved2 });
8936
+ } else {
8937
+ send();
8938
+ }
8898
8939
  }
8899
8940
  blocking(pgid) {
8900
8941
  for (const { ppid } of this.children.values()) if (this.pgidOf(ppid) === pgid) return true;
@@ -10164,11 +10205,8 @@ var Launcher = class {
10164
10205
  locks = new LockTable();
10165
10206
  settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
10166
10207
  ptys = new PtyTable(
10167
- (tty, sig) => this.settling.deliver(
10168
- sig,
10169
- this.jobs.tcgetpgrp(tty, 0),
10170
- () => this.jobs.signalOwnedForeground(tty, sig)
10171
- )
10208
+ (tty, sig) => this.keySignal(tty, 0, sig, () => this.jobs.signalOwnedForeground(tty, sig)),
10209
+ (tty) => this.settling.reading(this.jobs.tcgetpgrp(tty, 0))
10172
10210
  );
10173
10211
  net = new LoopbackNet();
10174
10212
  cdp;
@@ -10584,6 +10622,11 @@ var Launcher = class {
10584
10622
  })
10585
10623
  );
10586
10624
  }
10625
+ keySignal(tty, fallback, sig, send) {
10626
+ const pgid = this.jobs.tcgetpgrp(tty, fallback);
10627
+ const foreground = () => this.jobs.tcgetpgrp(tty, fallback);
10628
+ this.settling.deliver(sig, pgid, send, tty.reading ? void 0 : foreground);
10629
+ }
10587
10630
  ignoredBy(pid, fork = false) {
10588
10631
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
10589
10632
  }
@@ -10779,12 +10822,9 @@ var Launcher = class {
10779
10822
  let leader = 0;
10780
10823
  const tty = new KernelTty(
10781
10824
  { write: (bytes) => options.onData(bytes.slice()) },
10782
- (sig) => this.settling.deliver(
10783
- sig,
10784
- this.jobs.tcgetpgrp(tty, leader),
10785
- () => this.jobs.signalForeground(tty, leader, sig)
10786
- )
10825
+ (sig) => this.keySignal(tty, leader, sig, () => this.jobs.signalForeground(tty, leader, sig))
10787
10826
  );
10827
+ tty.onRead = () => this.settling.reading(this.jobs.tcgetpgrp(tty, leader));
10788
10828
  tty.name = `/dev/tty${++this.terminals}`;
10789
10829
  tty.setSize(options.cols ?? 80, options.rows ?? 24);
10790
10830
  const fds = new FdTable();