@ai-ecoverse/slicc-kernel 1.124.7 → 1.124.8

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
@@ -5776,6 +5776,7 @@ var LOOKUP_OPS = [
5776
5776
  "realpath"
5777
5777
  ];
5778
5778
  var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
5779
+ var TOUCHED_MAX = 4096;
5779
5780
  var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5780
5781
  var lockName = (instance) => `slicc-unlinked:${instance}`;
5781
5782
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
@@ -5814,6 +5815,9 @@ var UnlinkedKeeper = class {
5814
5815
  queue = Promise.resolve();
5815
5816
  count = 0;
5816
5817
  replacing = /* @__PURE__ */ new Map();
5818
+ gen = 0;
5819
+ floor = 0;
5820
+ touched = /* @__PURE__ */ new Map();
5817
5821
  replaced = 0;
5818
5822
  links = /* @__PURE__ */ new Map();
5819
5823
  linked = /* @__PURE__ */ new Map();
@@ -5840,28 +5844,28 @@ var UnlinkedKeeper = class {
5840
5844
  const { replacing } = this;
5841
5845
  const replace = async (target, op) => {
5842
5846
  const { promise, resolve: resolve2 } = Promise.withResolvers();
5847
+ keeper.touch(target);
5843
5848
  let mine;
5844
5849
  const gated = () => {
5845
5850
  const before = replacing.get(target);
5846
- mine = { done: Promise.all([before?.done, promise]).then(() => void 0) };
5851
+ mine = { done: Promise.all([before?.done, promise]).then(() => void 0), sealed: false };
5847
5852
  replacing.set(target, mine);
5848
5853
  };
5854
+ const sealed = () => {
5855
+ if (mine) mine.sealed = true;
5856
+ };
5849
5857
  try {
5850
- await op(gated);
5858
+ await op(gated, sealed);
5851
5859
  } finally {
5852
5860
  keeper.replaced += 1;
5861
+ keeper.touch(target);
5853
5862
  resolve2();
5854
5863
  if (mine && replacing.get(target) === mine) replacing.delete(target);
5855
5864
  }
5856
5865
  };
5857
5866
  const unreplaced = async (path, op) => {
5858
5867
  const at = fs.resolvePath("/", path);
5859
- const pending = async () => {
5860
- for (let p = at; replacing.size > 0; p = parentOf5(p)) {
5861
- await replacing.get(p)?.done;
5862
- if (p === "/") return;
5863
- }
5864
- };
5868
+ const pending = () => keeper.settled(at);
5865
5869
  for (let attempt = 1; ; attempt++) {
5866
5870
  await pending();
5867
5871
  const seen = keeper.replaced;
@@ -5895,8 +5899,8 @@ var UnlinkedKeeper = class {
5895
5899
  lstat: (path) => unreplaced(path, async () => counted3(await fs.lstat(path))),
5896
5900
  async rm(path, options) {
5897
5901
  const target = fs.resolvePath("/", path);
5898
- await replace(target, async (gated) => {
5899
- const pinned = await keeper.pin(target, gated);
5902
+ await replace(target, async (gated, sealed) => {
5903
+ const pinned = await keeper.pin(target, gated, sealed);
5900
5904
  if (pinned.moved.has(target)) pinned.commit();
5901
5905
  else await keeper.settle(pinned, () => fs.rm(path, options));
5902
5906
  });
@@ -5904,8 +5908,8 @@ var UnlinkedKeeper = class {
5904
5908
  async rename(from, to) {
5905
5909
  const target = fs.resolvePath("/", to);
5906
5910
  if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
5907
- await replace(target, async (gated) => {
5908
- const pinned = await keeper.pin(target, gated);
5911
+ await replace(target, async (gated, sealed) => {
5912
+ const pinned = await keeper.pin(target, gated, sealed);
5909
5913
  await keeper.settle(pinned, () => fs.rename(from, to));
5910
5914
  });
5911
5915
  },
@@ -5913,6 +5917,39 @@ var UnlinkedKeeper = class {
5913
5917
  readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted3(st)])
5914
5918
  };
5915
5919
  }
5920
+ async settled(path) {
5921
+ for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
5922
+ await this.replacing.get(p)?.done;
5923
+ if (p === "/") return;
5924
+ }
5925
+ }
5926
+ generation() {
5927
+ return this.gen;
5928
+ }
5929
+ changedSince(path, gen) {
5930
+ if (gen < this.floor) return true;
5931
+ for (let p = path; this.touched.size > 0; p = parentOf5(p)) {
5932
+ if ((this.touched.get(p) ?? 0) > gen) return true;
5933
+ if (p === "/") return false;
5934
+ }
5935
+ return false;
5936
+ }
5937
+ touch(target) {
5938
+ this.gen += 1;
5939
+ this.touched.delete(target);
5940
+ this.touched.set(target, this.gen);
5941
+ if (this.touched.size <= TOUCHED_MAX) return;
5942
+ const [oldest, at] = this.touched.entries().next().value;
5943
+ this.touched.delete(oldest);
5944
+ this.floor = at;
5945
+ }
5946
+ missed(path) {
5947
+ for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
5948
+ if (this.replacing.get(p)?.sealed) return true;
5949
+ if (p === "/") return false;
5950
+ }
5951
+ return false;
5952
+ }
5916
5953
  async settle(pinned, op) {
5917
5954
  try {
5918
5955
  await op();
@@ -5933,33 +5970,41 @@ var UnlinkedKeeper = class {
5933
5970
  }
5934
5971
  return held;
5935
5972
  }
5936
- async pin(target, gated = () => void 0) {
5973
+ async pin(target, gated = () => void 0, sealed = () => void 0) {
5937
5974
  const paths = [];
5938
5975
  const releases = [];
5939
- for (; ; ) {
5940
- const fresh2 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
5941
- if (fresh2.length === 0) break;
5942
- fresh2.sort();
5943
- if (paths.some((path) => path > fresh2[0])) {
5944
- for (const release of releases.splice(0)) release();
5945
- fresh2.push(...paths.splice(0));
5976
+ const take = async () => {
5977
+ for (; ; ) {
5978
+ const fresh2 = [...this.heldWithin(target).keys()].filter((path) => !paths.includes(path));
5979
+ if (fresh2.length === 0) return;
5946
5980
  fresh2.sort();
5981
+ if (paths.some((path) => path > fresh2[0])) {
5982
+ for (const release of releases.splice(0)) release();
5983
+ fresh2.push(...paths.splice(0));
5984
+ fresh2.sort();
5985
+ }
5986
+ for (const path of fresh2) {
5987
+ paths.push(path);
5988
+ releases.push(await this.gate.acquire(path));
5989
+ }
5947
5990
  }
5948
- for (const path of fresh2) {
5949
- paths.push(path);
5950
- releases.push(await this.gate.acquire(path));
5951
- }
5952
- }
5991
+ };
5992
+ await take();
5953
5993
  gated();
5954
- const now = this.heldWithin(target);
5955
- const held = new Map(
5956
- paths.flatMap((path) => now.has(path) ? [[path, now.get(path)]] : [])
5957
- );
5994
+ const held = /* @__PURE__ */ new Map();
5958
5995
  const hidden = /* @__PURE__ */ new Map();
5959
- for (const path of held.keys()) {
5960
- const at = await this.hide(path);
5961
- if (at !== void 0) hidden.set(path, at);
5996
+ for (; ; ) {
5997
+ await take();
5998
+ const now = this.heldWithin(target);
5999
+ const next3 = paths.filter((path) => now.has(path) && !held.has(path));
6000
+ if (next3.length === 0) break;
6001
+ for (const path of next3) {
6002
+ held.set(path, now.get(path));
6003
+ const at = await this.hide(path);
6004
+ if (at !== void 0) hidden.set(path, at);
6005
+ }
5962
6006
  }
6007
+ sealed();
5963
6008
  const done4 = () => {
5964
6009
  for (const release of releases) release();
5965
6010
  };
@@ -7025,7 +7070,7 @@ function satisfies(version, range2) {
7025
7070
  }
7026
7071
 
7027
7072
  // src/version.ts
7028
- var KERNEL_VERSION = true ? "1.124.7" : "0.0.0-dev";
7073
+ var KERNEL_VERSION = true ? "1.124.8" : "0.0.0-dev";
7029
7074
  var OS_RELEASE = "6.0.0-slicc";
7030
7075
  function procVersion() {
7031
7076
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -10409,6 +10454,9 @@ var HeldPaths = class extends Map {
10409
10454
  keptFor(handle) {
10410
10455
  return this.keptHandles.get(handle);
10411
10456
  }
10457
+ holdsHandle(handle) {
10458
+ return this.handles.has(handle);
10459
+ }
10412
10460
  keeps(hidden) {
10413
10461
  for (const file of this.uses.keys()) if (file.hidden === hidden) return true;
10414
10462
  for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
@@ -11855,10 +11903,16 @@ async function run(entry, path, req) {
11855
11903
  return done;
11856
11904
  case "exists":
11857
11905
  return json2(await fs.exists(path));
11858
- case "stat":
11859
- return json2(statJson(await shownAs(entry, path, await fs.stat(path))));
11860
- case "lstat":
11861
- return json2(statJson(await shownAs(entry, path, await fs.lstat(path))));
11906
+ case "stat": {
11907
+ const gen = entry.replacements?.generation();
11908
+ const st = statJson(await shownAs(entry, path, await fs.stat(path)));
11909
+ return json2(gen === void 0 ? st : { ...st, gen });
11910
+ }
11911
+ case "lstat": {
11912
+ const gen = entry.replacements?.generation();
11913
+ const st = statJson(await shownAs(entry, path, await fs.lstat(path)));
11914
+ return json2(gen === void 0 ? st : { ...st, gen });
11915
+ }
11862
11916
  case "readdir":
11863
11917
  return json2(await fs.readdir(path));
11864
11918
  case "readdir-stat":
@@ -12033,14 +12087,26 @@ async function opening(entry, path, mode) {
12033
12087
  await need(g, "open", path, want, { directory: false });
12034
12088
  }
12035
12089
  }
12090
+ async function stillThere(entry, path, req) {
12091
+ if (req.gen === void 0 || !entry.replacements?.changedSince(path, req.gen)) return;
12092
+ const st = await entry.fs.stat(path).catch(() => void 0);
12093
+ if (!st) throw syncError("ENOENT", path);
12094
+ }
12036
12095
  async function holdOp(entry, path, req) {
12037
12096
  const mode = req.mode ?? 0;
12038
12097
  const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
12039
12098
  if (on && open) {
12040
- await opening(entry, path, mode);
12041
- const real = await entry.fs.realpath?.(path, true).catch(() => path);
12042
- await entry.source?.open(path, real);
12043
- return json2(entry.hold?.(path, on, open, real) ?? null);
12099
+ for (; ; ) {
12100
+ const r = entry.replacements;
12101
+ await r?.settled(path);
12102
+ const gen = r?.generation() ?? 0;
12103
+ await opening(entry, path, mode);
12104
+ const real = await entry.fs.realpath?.(path, true).catch(() => path);
12105
+ await entry.source?.open(path, real);
12106
+ await stillThere(entry, path, req);
12107
+ if (!r || !r.missed(path) && !r.changedSince(path, gen))
12108
+ return json2(entry.hold?.(path, on, open, real) ?? null);
12109
+ }
12044
12110
  }
12045
12111
  entry.hold?.(path, on, open, void 0, req.handle);
12046
12112
  return done;
@@ -17621,6 +17687,7 @@ var VfsNode = class {
17621
17687
  path;
17622
17688
  orphaned;
17623
17689
  id;
17690
+ ino;
17624
17691
  onDirty;
17625
17692
  onDirtyState;
17626
17693
  counted = false;
@@ -17973,8 +18040,12 @@ var VfsNodes = class {
17973
18040
  if (id2 !== void 0) this.know(node, id2);
17974
18041
  return node;
17975
18042
  }
17976
- open(path, pin, writable2 = false, id2) {
17977
- const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
18043
+ open(path, pin, writable2 = false, id2, ino) {
18044
+ const found = this.named(path, id2);
18045
+ const same = !found || ino === void 0 || found.ino === void 0 || found.ino === ino;
18046
+ if (found && !same) this.orphan(found);
18047
+ const node = (same ? found : void 0) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
18048
+ node.ino ??= ino;
17978
18049
  node.opens++;
17979
18050
  if (writable2) node.writers++;
17980
18051
  return node;
@@ -18268,7 +18339,7 @@ function vfsFile(fs, opts, nodes) {
18268
18339
  const access2 = opts.flags & O_ACCMODE;
18269
18340
  const readable2 = access2 !== O_WRONLY;
18270
18341
  const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
18271
- const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable2, opts.id) : nodes.open(opts.path, opts.pin, writable2, opts.id);
18342
+ const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable2, opts.id) : nodes.open(opts.path, opts.pin, writable2, opts.id, opts.ino);
18272
18343
  let openError;
18273
18344
  const atOpen = (op) => {
18274
18345
  void node.serial(op).catch((err) => {
@@ -19038,6 +19109,7 @@ var WasmProcess = class {
19038
19109
  const fd = this.fds.install(heldFile(), 3);
19039
19110
  try {
19040
19111
  const nodes = this.nodes;
19112
+ const gen = this.replacements?.generation() ?? 0;
19041
19113
  const path = await this.openPath(req, marked);
19042
19114
  if (inProc(path)) {
19043
19115
  this.fds.installAt(fd, await this.procFile(req, path));
@@ -19062,7 +19134,15 @@ var WasmProcess = class {
19062
19134
  }
19063
19135
  });
19064
19136
  } else {
19065
- this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
19137
+ await this.unmissed(
19138
+ () => this.openPath(req, marked),
19139
+ async (at) => {
19140
+ await this.stillOpen(at, req.handle);
19141
+ return nodes.identify(at);
19142
+ },
19143
+ (id2, at) => this.fds.installAt(fd, this.openVfsFile({ ...open, path: at }, id2, fs)),
19144
+ { at: path, gen }
19145
+ );
19066
19146
  }
19067
19147
  if (req.existing && !await present2(fs, path, true)) {
19068
19148
  throw new KernelError("ENOENT");
@@ -19243,7 +19323,8 @@ var WasmProcess = class {
19243
19323
  ...req.truncate ? { truncate: true } : {},
19244
19324
  ...req.create ? { create: true } : {},
19245
19325
  ...req.pin ? { pin: req.pin } : {},
19246
- ...id2 !== void 0 && !req.orphan ? { id: id2 } : {}
19326
+ ...id2 !== void 0 && !req.orphan ? { id: id2 } : {},
19327
+ ...req.ino !== void 0 && !req.orphan ? { ino: req.ino } : {}
19247
19328
  },
19248
19329
  this.nodes
19249
19330
  );
@@ -19414,9 +19495,27 @@ var WasmProcess = class {
19414
19495
  return false;
19415
19496
  }
19416
19497
  keptHandle;
19498
+ holdsHandle;
19499
+ replacements;
19500
+ async unmissed(path, look, make, first) {
19501
+ let given = first;
19502
+ for (; ; ) {
19503
+ const r = this.replacements;
19504
+ const gen = given?.gen ?? r?.generation() ?? 0;
19505
+ const at = given?.at ?? await path();
19506
+ given = void 0;
19507
+ await r?.settled(at);
19508
+ const found = await look(at);
19509
+ if (!r || !r.missed(at) && !r.changedSince(at, gen)) return make(found, at);
19510
+ }
19511
+ }
19512
+ async stillOpen(at, handle) {
19513
+ if (handle === void 0 || this.keptHandle?.(handle) !== void 0) return;
19514
+ if (this.holdsHandle?.(handle) !== false) return;
19515
+ if (!await present2(this.options.fs, at, true)) throw new KernelError("ENOENT");
19516
+ }
19417
19517
  async keptOr(path, handle) {
19418
- const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
19419
- if (held !== void 0) return held;
19518
+ if (handle !== void 0) return this.keptHandle?.(handle) ?? path;
19420
19519
  const kept = this.keptPath?.(path);
19421
19520
  return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
19422
19521
  }
@@ -19426,25 +19525,37 @@ var WasmProcess = class {
19426
19525
  await this.fds.dup2Clipboard(req.share, req.fd);
19427
19526
  return;
19428
19527
  }
19429
- if (req.path === void 0) throw new KernelError("EINVAL");
19430
- const path = req.orphan ? req.path : await this.keptOr(await this.nodes.targetKey(req.path), req.handle);
19431
- const id2 = req.orphan ? void 0 : await this.nodes.identify(path);
19432
- if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
19433
- const file = vfsFile(
19434
- this.options.fs,
19435
- {
19436
- path,
19437
- flags: req.flags ?? 0,
19438
- position: req.position ?? 0,
19439
- ...req.contents !== void 0 ? { contents: req.contents } : {},
19440
- ...req.orphan ? { orphan: true } : {},
19441
- ...req.dirty ? { dirty: true } : {},
19442
- ...req.pin ? { pin: req.pin } : {},
19443
- ...id2 !== void 0 ? { id: id2 } : {}
19528
+ const asked = req.path;
19529
+ if (asked === void 0) throw new KernelError("EINVAL");
19530
+ const install = ([path, id2]) => {
19531
+ if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
19532
+ const file = vfsFile(
19533
+ this.options.fs,
19534
+ {
19535
+ path,
19536
+ flags: req.flags ?? 0,
19537
+ position: req.position ?? 0,
19538
+ ...req.contents !== void 0 ? { contents: req.contents } : {},
19539
+ ...req.orphan ? { orphan: true } : {},
19540
+ ...req.dirty ? { dirty: true } : {},
19541
+ ...req.pin ? { pin: req.pin } : {},
19542
+ ...id2 !== void 0 ? { id: id2 } : {},
19543
+ ...req.ino !== void 0 && path === asked ? { ino: req.ino } : {}
19544
+ },
19545
+ this.nodes
19546
+ );
19547
+ this.fds.installAt(req.fd, file);
19548
+ };
19549
+ if (req.orphan) return install([asked, void 0]);
19550
+ await this.unmissed(
19551
+ () => this.nodes.targetKey(asked),
19552
+ async (target) => {
19553
+ const at = await this.keptOr(target, req.handle);
19554
+ await this.stillOpen(at, req.handle);
19555
+ return [at, await this.nodes.identify(at)];
19444
19556
  },
19445
- this.nodes
19557
+ install
19446
19558
  );
19447
- this.fds.installAt(req.fd, file);
19448
19559
  }
19449
19560
  reserve(fd, min = 3, meta) {
19450
19561
  if (fd === void 0) return this.fds.install(heldFile(meta), Math.max(3, min));
@@ -20097,6 +20208,12 @@ function inherited(opts) {
20097
20208
  ...opts.cred ? { cred: opts.cred } : {}
20098
20209
  };
20099
20210
  }
20211
+ function heldBy(process2, holds2, replacements) {
20212
+ process2.keptPath = (path) => holds2.keptAt(path)?.hidden;
20213
+ process2.keptHandle = (handle) => holds2.keptFor(handle)?.hidden;
20214
+ process2.holdsHandle = (handle) => holds2.holdsHandle(handle);
20215
+ if (replacements) process2.replacements = replacements;
20216
+ }
20100
20217
  function holdingToken(opts, holds2, caller) {
20101
20218
  return mintSyncFsToken({
20102
20219
  ...opts.owners ? caller : { umask: caller.umask },
@@ -20104,6 +20221,7 @@ function holdingToken(opts, holds2, caller) {
20104
20221
  ...opts.links !== void 0 ? { links: opts.links } : {},
20105
20222
  ...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
20106
20223
  ...opts.source ? { source: opts.source } : {},
20224
+ ...opts.replacements ? { replacements: opts.replacements } : {},
20107
20225
  fs: opts.fs,
20108
20226
  noLinks: opts.noLinks,
20109
20227
  cwd: opts.cwd,
@@ -20242,8 +20360,7 @@ function spawnWasmProcess(opts) {
20242
20360
  created: created3,
20243
20361
  holdsKept: async (path) => path.includes(`/${UNLINKED_DIR}/`) && (holds2.keeps(path) || await process2.holds(path))
20244
20362
  });
20245
- process2.keptPath = (path) => holds2.keptAt(path)?.hidden;
20246
- process2.keptHandle = (handle2) => holds2.keptFor(handle2)?.hidden;
20363
+ heldBy(process2, holds2, opts.replacements);
20247
20364
  const worker = opts.createWorker();
20248
20365
  const dying = new Dying();
20249
20366
  const dispatch2 = (req, block) => dying.serve(
@@ -25431,6 +25548,7 @@ var Launcher = class {
25431
25548
  nodes: stack.nodes,
25432
25549
  held: this.held,
25433
25550
  unlinkGate: stack.unlinked.gate,
25551
+ replacements: stack.unlinked,
25434
25552
  statfs: (path) => this.mounts.statfs(path),
25435
25553
  mounts: () => this.mounts.list(),
25436
25554
  mount: (call, signal) => this.processMount(pid, call, signal),