@ai-ecoverse/slicc-kernel 1.98.4 → 1.99.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/dist/node.js CHANGED
@@ -1038,6 +1038,7 @@ function kernelFdKind(file) {
1038
1038
  }
1039
1039
  var FdTable = class _FdTable {
1040
1040
  static MAX_FDS = 1024;
1041
+ handles;
1041
1042
  fds = /* @__PURE__ */ new Map();
1042
1043
  cloexec = /* @__PURE__ */ new Set();
1043
1044
  status = /* @__PURE__ */ new Map();
@@ -1134,6 +1135,7 @@ var FdTable = class _FdTable {
1134
1135
  for (const [fd, file] of this.fds) child.fds.set(fd, file.retain());
1135
1136
  for (const fd of this.cloexec) child.cloexec.add(fd);
1136
1137
  for (const [fd, flags2] of this.status) child.status.set(fd, flags2);
1138
+ child.handles = this.handles?.fork();
1137
1139
  return child;
1138
1140
  }
1139
1141
  async closeAll() {
@@ -1141,6 +1143,7 @@ var FdTable = class _FdTable {
1141
1143
  this.fds.clear();
1142
1144
  this.cloexec.clear();
1143
1145
  this.status.clear();
1146
+ this.handles?.clear();
1144
1147
  await Promise.allSettled(files.map((file) => Promise.resolve().then(() => file.release())));
1145
1148
  }
1146
1149
  };
@@ -11075,7 +11078,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
11075
11078
  var EARLY_WHOLE_FILE = 64 * 1024;
11076
11079
 
11077
11080
  // src/version.ts
11078
- var KERNEL_VERSION = true ? "1.98.4" : "0.0.0-dev";
11081
+ var KERNEL_VERSION = true ? "1.99.0" : "0.0.0-dev";
11079
11082
  var OS_RELEASE = "6.0.0-slicc";
11080
11083
  function procVersion() {
11081
11084
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -14173,6 +14176,206 @@ async function waitReady(fds, read, write, deadline, interrupted3) {
14173
14176
  }
14174
14177
  }
14175
14178
 
14179
+ // src/kernel/sem.ts
14180
+ var SEM_VALUE_MAX = 2147483647;
14181
+ var SEM_NAMES_MAX = 4096;
14182
+ var SEM_HANDLES_MAX = 256;
14183
+ var NAME_MAX = 251;
14184
+ var TIMER_MAX_MS = 2147483647;
14185
+ var SEM_OPS = [
14186
+ "sem-open",
14187
+ "sem-close",
14188
+ "sem-unlink",
14189
+ "sem-post",
14190
+ "sem-wait",
14191
+ "sem-getvalue"
14192
+ ];
14193
+ var Semaphore = class {
14194
+ value;
14195
+ uid;
14196
+ gid;
14197
+ mode;
14198
+ waiters = [];
14199
+ constructor(value, uid, gid, mode) {
14200
+ this.value = value;
14201
+ this.uid = uid;
14202
+ this.gid = gid;
14203
+ this.mode = mode;
14204
+ }
14205
+ post() {
14206
+ const next2 = this.waiters.shift();
14207
+ if (next2) next2.grant();
14208
+ else if (this.value >= SEM_VALUE_MAX) throw new KernelError("EOVERFLOW");
14209
+ else this.value++;
14210
+ }
14211
+ take() {
14212
+ if (this.value === 0) return false;
14213
+ this.value--;
14214
+ return true;
14215
+ }
14216
+ wait(signal, timeoutMs) {
14217
+ if (this.take()) return Promise.resolve(true);
14218
+ if (signal.aborted) return Promise.reject(new KernelError("EINTR"));
14219
+ return new Promise((resolve2, reject) => {
14220
+ const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : performance.now() + timeoutMs;
14221
+ let timer;
14222
+ const leave = () => {
14223
+ const at = this.waiters.indexOf(waiter);
14224
+ if (at >= 0) this.waiters.splice(at, 1);
14225
+ clearTimeout(timer);
14226
+ signal.removeEventListener("abort", abort);
14227
+ };
14228
+ const waiter = {
14229
+ grant: () => {
14230
+ leave();
14231
+ resolve2(true);
14232
+ }
14233
+ };
14234
+ const abort = () => {
14235
+ leave();
14236
+ reject(new KernelError("EINTR"));
14237
+ };
14238
+ const expire = () => {
14239
+ const left = deadline - performance.now();
14240
+ if (left > 0) {
14241
+ timer = setTimeout(expire, Math.min(left, TIMER_MAX_MS));
14242
+ return;
14243
+ }
14244
+ leave();
14245
+ resolve2(false);
14246
+ };
14247
+ this.waiters.push(waiter);
14248
+ signal.addEventListener("abort", abort, { once: true });
14249
+ if (deadline !== Number.POSITIVE_INFINITY) {
14250
+ timer = setTimeout(expire, Math.min(timeoutMs, TIMER_MAX_MS));
14251
+ }
14252
+ });
14253
+ }
14254
+ };
14255
+ var SemTable = class {
14256
+ names = /* @__PURE__ */ new Map();
14257
+ limit;
14258
+ constructor(limit = SEM_NAMES_MAX) {
14259
+ this.limit = limit;
14260
+ }
14261
+ open(name2, create, exclusive, make, check, adopt) {
14262
+ const found = this.names.get(name2);
14263
+ if (found) {
14264
+ if (create && exclusive) throw new KernelError("EEXIST");
14265
+ check(found);
14266
+ return adopt(found);
14267
+ }
14268
+ if (!create) throw new KernelError("ENOENT");
14269
+ if (this.names.size >= this.limit) throw new KernelError("ENOSPC");
14270
+ const sem = make();
14271
+ const handle = adopt(sem);
14272
+ this.names.set(name2, sem);
14273
+ return handle;
14274
+ }
14275
+ unlink(name2, check) {
14276
+ const found = this.names.get(name2);
14277
+ if (!found) throw new KernelError("ENOENT");
14278
+ check(found);
14279
+ this.names.delete(name2);
14280
+ }
14281
+ };
14282
+ var SemHandles = class _SemHandles {
14283
+ byHandle = /* @__PURE__ */ new Map();
14284
+ next = 1;
14285
+ add(sem) {
14286
+ for (const [handle2, entry] of this.byHandle) {
14287
+ if (entry.sem === sem) {
14288
+ entry.opens++;
14289
+ return handle2;
14290
+ }
14291
+ }
14292
+ if (this.byHandle.size >= SEM_HANDLES_MAX) throw new KernelError("EMFILE");
14293
+ const handle = this.next++;
14294
+ this.byHandle.set(handle, { sem, opens: 1 });
14295
+ return handle;
14296
+ }
14297
+ get(handle) {
14298
+ const entry = this.byHandle.get(handle);
14299
+ if (!entry) throw new KernelError("EINVAL");
14300
+ return entry.sem;
14301
+ }
14302
+ close(handle) {
14303
+ const entry = this.byHandle.get(handle);
14304
+ if (!entry) throw new KernelError("EINVAL");
14305
+ if (--entry.opens === 0) this.byHandle.delete(handle);
14306
+ }
14307
+ fork() {
14308
+ const child = new _SemHandles();
14309
+ child.next = this.next;
14310
+ for (const [handle, entry] of this.byHandle) child.byHandle.set(handle, { ...entry });
14311
+ return child;
14312
+ }
14313
+ clear() {
14314
+ this.byHandle.clear();
14315
+ }
14316
+ };
14317
+ function semName(raw) {
14318
+ const name2 = raw.replace(/^\/+/, "");
14319
+ if (name2 === "" || name2.includes("/") || name2 === "." || name2 === "..") {
14320
+ throw new KernelError("EINVAL");
14321
+ }
14322
+ if (new TextEncoder().encode(name2).length > NAME_MAX) throw new KernelError("ENAMETOOLONG");
14323
+ return name2;
14324
+ }
14325
+ var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
14326
+ async function semSyscall(req, proc) {
14327
+ const { table, handles, cred } = proc;
14328
+ if (!table) throw new KernelError("ENOSYS");
14329
+ const { denied: denied2 } = proc;
14330
+ const shown = (name2) => `/dev/shm/sem.${name2}`;
14331
+ const opens = (name2) => (sem) => {
14332
+ const perm = { uid: sem.uid, gid: sem.gid, mode: sem.mode };
14333
+ if (denied2 && !permits(cred, perm, R_OK | W_OK, false)) denied2("sem_open", shown(name2));
14334
+ };
14335
+ const unlinks = (name2) => (sem) => {
14336
+ if (denied2 && cred.euid !== 0 && cred.euid !== sem.uid) denied2("sem_unlink", shown(name2));
14337
+ };
14338
+ switch (req.op) {
14339
+ case "sem-open": {
14340
+ if (req.value < 0 || req.value > SEM_VALUE_MAX) throw new KernelError("EINVAL");
14341
+ const name2 = semName(req.name);
14342
+ const mode = req.mode & 511 & ~proc.umask;
14343
+ return ok2(
14344
+ table.open(
14345
+ name2,
14346
+ req.create,
14347
+ req.exclusive,
14348
+ () => new Semaphore(req.value, cred.euid, cred.egid, mode),
14349
+ opens(name2),
14350
+ (sem) => handles.add(sem)
14351
+ )
14352
+ );
14353
+ }
14354
+ case "sem-close":
14355
+ handles.close(req.handle);
14356
+ return ok2();
14357
+ case "sem-unlink": {
14358
+ const name2 = semName(req.name);
14359
+ table.unlink(name2, unlinks(name2));
14360
+ return ok2();
14361
+ }
14362
+ case "sem-post":
14363
+ handles.get(req.handle).post();
14364
+ return ok2();
14365
+ case "sem-getvalue":
14366
+ return ok2(handles.get(req.handle).value);
14367
+ case "sem-wait": {
14368
+ const sem = handles.get(req.handle);
14369
+ if (sem.take()) return ok2();
14370
+ if (req.how === "try") throw new KernelError("EAGAIN");
14371
+ if (req.how === "timed" && req.timeoutMs === 0) throw new KernelError("ETIMEDOUT");
14372
+ const got = await sem.wait(proc.blocking(), req.how === "block" ? -1 : req.timeoutMs);
14373
+ if (!got) throw new KernelError("ETIMEDOUT");
14374
+ return ok2();
14375
+ }
14376
+ }
14377
+ }
14378
+
14176
14379
  // src/kernel/net/datagram-wire.ts
14177
14380
  function encodeReceived(got) {
14178
14381
  const host = new TextEncoder().encode(got.host);
@@ -14219,20 +14422,20 @@ function dgramAt(fds, fd) {
14219
14422
  if (!(file instanceof KernelDgram)) throw new KernelError("EOPNOTSUPP");
14220
14423
  return file;
14221
14424
  }
14222
- var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
14425
+ var ok3 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
14223
14426
  async function socketSyscall(req, proc) {
14224
14427
  const { fds, net } = proc;
14225
14428
  switch (req.op) {
14226
14429
  case "sock-open": {
14227
14430
  if (req.type === "dgram" && req.domain !== "inet") throw new KernelError("EPROTONOSUPPORT");
14228
14431
  const file = req.type === "dgram" ? new KernelDgram(net) : net.socket(req.domain);
14229
- return ok2(fds.install(new OpenFile(file), 3));
14432
+ return ok3(fds.install(new OpenFile(file), 3));
14230
14433
  }
14231
14434
  case "sock-pair": {
14232
14435
  const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
14233
14436
  const first = fds.install(new OpenFile(a), 3);
14234
14437
  try {
14235
- return ok2([first, fds.install(new OpenFile(b), 3)]);
14438
+ return ok3([first, fds.install(new OpenFile(b), 3)]);
14236
14439
  } catch (e) {
14237
14440
  await Promise.resolve(fds.close(first));
14238
14441
  throw e;
@@ -14240,32 +14443,32 @@ async function socketSyscall(req, proc) {
14240
14443
  }
14241
14444
  case "sock-bind":
14242
14445
  anySocketAt(fds, req.fd).bind(req.addr);
14243
- return ok2();
14446
+ return ok3();
14244
14447
  case "sock-listen":
14245
14448
  socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
14246
- return ok2();
14449
+ return ok3();
14247
14450
  case "sock-accept":
14248
- return ok2(await accept(proc, req.fd, req.nonblock));
14451
+ return ok3(await accept(proc, req.fd, req.nonblock));
14249
14452
  case "sock-connect":
14250
14453
  return connect(req, proc);
14251
14454
  case "sock-shutdown":
14252
14455
  anySocketAt(fds, req.fd).shutdown(req.how);
14253
- return ok2();
14456
+ return ok3();
14254
14457
  case "sock-name": {
14255
14458
  const socket = anySocketAt(fds, req.fd);
14256
14459
  const addr = req.peer ? socket.peer : socket.local;
14257
14460
  if (req.peer && !addr) throw new KernelError("ENOTCONN");
14258
- return ok2(addr ?? unnamed(socket instanceof KernelDgram ? "inet" : socket.domain));
14461
+ return ok3(addr ?? unnamed(socket instanceof KernelDgram ? "inet" : socket.domain));
14259
14462
  }
14260
14463
  case "sock-getopt":
14261
- return ok2(anySocketAt(fds, req.fd).getOption(req.level, req.name));
14464
+ return ok3(anySocketAt(fds, req.fd).getOption(req.level, req.name));
14262
14465
  case "sock-setopt":
14263
14466
  anySocketAt(fds, req.fd).setOption(req.level, req.name, req.value);
14264
- return ok2();
14467
+ return ok3();
14265
14468
  case "sock-resolve": {
14266
14469
  const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
14267
14470
  if (addresses.length === 0) throw new KernelError("ENOENT");
14268
- return ok2(addresses);
14471
+ return ok3(addresses);
14269
14472
  }
14270
14473
  default:
14271
14474
  return datagram(req, proc);
@@ -14275,7 +14478,7 @@ async function connect(req, proc) {
14275
14478
  const target = anySocketAt(proc.fds, req.fd);
14276
14479
  if (target instanceof KernelDgram) {
14277
14480
  target.connectTo(req.addr);
14278
- return ok2();
14481
+ return ok3();
14279
14482
  }
14280
14483
  const stamp = proc.net.facadeAt(req.addr) && await proc.stamp?.();
14281
14484
  const dialing = target.connect(req.addr, true, proc.cred?.(), stamp || void 0);
@@ -14284,13 +14487,13 @@ async function connect(req, proc) {
14284
14487
  throw new KernelError("EINPROGRESS");
14285
14488
  }
14286
14489
  if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
14287
- return ok2();
14490
+ return ok3();
14288
14491
  }
14289
14492
  async function datagram(req, proc) {
14290
14493
  const dgram = dgramAt(proc.fds, req.fd);
14291
14494
  if (req.op === "sock-sendto") {
14292
14495
  if (req.nonblock && !pollFile(dgram).writable) throw new KernelError("EAGAIN");
14293
- return ok2(await dgram.send(req.body, req.addr));
14496
+ return ok3(await dgram.send(req.body, req.addr));
14294
14497
  }
14295
14498
  const ready2 = pollFile(dgram).readable;
14296
14499
  if (req.nonblock && !ready2) throw new KernelError("EAGAIN");
@@ -15056,6 +15259,9 @@ function isHttpSyscall(req) {
15056
15259
  function isSocketSyscall(req) {
15057
15260
  return req.op.startsWith("sock-");
15058
15261
  }
15262
+ function isSemSyscall(req) {
15263
+ return req.op.startsWith("sem-");
15264
+ }
15059
15265
  function isEpollSyscall(req) {
15060
15266
  return EPOLL_OPS.includes(req.op);
15061
15267
  }
@@ -15150,6 +15356,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
15150
15356
  "sig-pause",
15151
15357
  ...SOCKET_OPS,
15152
15358
  ...EPOLL_OPS,
15359
+ ...SEM_OPS,
15153
15360
  ...HTTP_OPS,
15154
15361
  ...PTY_OPS,
15155
15362
  ...HOST_OPS
@@ -15382,6 +15589,7 @@ var WasmProcess = class {
15382
15589
  if (isTtySyscall(req)) return this.ttySyscall(req);
15383
15590
  if (isJobSyscall(req)) return this.jobSyscall(req);
15384
15591
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
15592
+ if (isSemSyscall(req)) return await this.semSyscall(req, cancelled);
15385
15593
  if (isEpollSyscall(req)) {
15386
15594
  return await epollSyscall(req, {
15387
15595
  fds: this.fds,
@@ -16101,6 +16309,20 @@ var WasmProcess = class {
16101
16309
  if (!http) return Promise.resolve({ ok: false, errno: "ENETUNREACH", message: NO_TRANSPORT });
16102
16310
  return http.syscall(req);
16103
16311
  }
16312
+ semSyscall(req, cancelled) {
16313
+ const g = this.guarded();
16314
+ const known = this.fds.handles;
16315
+ const handles = known instanceof SemHandles ? known : new SemHandles();
16316
+ this.fds.handles = handles;
16317
+ return semSyscall(req, {
16318
+ table: this.options.sems,
16319
+ handles,
16320
+ cred: this.cred,
16321
+ denied: g ? (op, path) => deny(g, op, path) : void 0,
16322
+ umask: this.umask,
16323
+ blocking: () => this.blockingSignal(cancelled)
16324
+ });
16325
+ }
16104
16326
  socketSyscall(req, cancelled) {
16105
16327
  this.net ??= this.options.net ?? new LoopbackNet();
16106
16328
  const resolver = this.options.resolver ?? HOSTS_ONLY;
@@ -16611,6 +16833,7 @@ function processOptions(opts, header4, hooks, created3) {
16611
16833
  net: opts.net,
16612
16834
  http: opts.http,
16613
16835
  locks: opts.locks,
16836
+ ...opts.sems ? { sems: opts.sems } : {},
16614
16837
  onReap: opts.onReap,
16615
16838
  resolver: opts.resolver,
16616
16839
  ...notifiers(header4),
@@ -20272,6 +20495,7 @@ var Launcher = class {
20272
20495
  sudoer;
20273
20496
  jobs = new JobTable();
20274
20497
  locks = new LockTable();
20498
+ sems = new SemTable();
20275
20499
  settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
20276
20500
  ptys = new PtyTable(
20277
20501
  (tty, sig) => this.keySignal(tty, 0, sig, () => this.jobs.signalOwnedForeground(tty, sig)),
@@ -21130,6 +21354,7 @@ var Launcher = class {
21130
21354
  net: this.net,
21131
21355
  http: new HttpHandles(this.transport, pid, this.loopbackFor(pid)),
21132
21356
  locks: this.locks,
21357
+ sems: this.sems,
21133
21358
  resolver: this.resolver,
21134
21359
  onReap: (child) => this.reaped(child, pid),
21135
21360
  ...req.fork ? { fork: req.fork } : {},