@ai-ecoverse/slicc-kernel 1.102.4 → 1.102.5

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
@@ -389,7 +389,7 @@ const kernel = await createKernel({ network: { transport: fetchTransport() } });
389
389
  await kernel.run(['curl', '-sS', 'https://registry.npmjs.org/@ai-ecoverse/wasm-bash/latest']);
390
390
  ```
391
391
 
392
- - **Sockets**: `AF_INET` stream sockets on `127.x` and `AF_UNIX` sockets are kernel descriptors on one loopback network per kernel, so processes can serve and connect to each other. They survive `dup`, `fork` and `exec` and work with `select`, `poll` and WASIX epoll, like pipe ends. A bind of an `AF_UNIX` socket creates its path in OPFS. Other addresses are unreachable (`ENETUNREACH`) unless the [uplink](#uplink) routes them: otherwise programs reach the outside world only through the proxy.
392
+ - **Sockets**: `AF_INET` stream sockets on `127.x` and `AF_UNIX` sockets are kernel descriptors on one loopback network per kernel, so processes can serve and connect to each other. They survive `dup`, `fork` and `exec` and work with `select`, `poll` and WASIX epoll, like pipe ends. A bind of an `AF_UNIX` socket creates its path in OPFS. A program's `connect` to a listener whose accept queue is full waits for a slot, as on Linux, instead of failing. A blocking connect waits, and a nonblocking one answers `EINPROGRESS` and turns writable once it's queued. After 30 s it fails with `ETIMEDOUT`, and it's `ECONNREFUSED` if the listener closes. Other addresses are unreachable (`ENETUNREACH`) unless the [uplink](#uplink) routes them: otherwise programs reach the outside world only through the proxy.
393
393
 
394
394
  #### Datagram sockets (UDP)
395
395
 
@@ -3564,6 +3564,7 @@ var KernelSocket = class _KernelSocket {
3564
3564
  backlog = 0;
3565
3565
  boundAt;
3566
3566
  changes = new Changes();
3567
+ room = new Changes();
3567
3568
  options = /* @__PURE__ */ new Map();
3568
3569
  constructor(net, domain) {
3569
3570
  this.net = net;
@@ -3648,7 +3649,10 @@ var KernelSocket = class _KernelSocket {
3648
3649
  for (; ; ) {
3649
3650
  if (this.state !== "listening") throw new KernelError("EINVAL");
3650
3651
  const next2 = this.queue.shift();
3651
- if (next2) return next2;
3652
+ if (next2) {
3653
+ this.room.wake();
3654
+ return next2;
3655
+ }
3652
3656
  if (nonblock) throw new KernelError("EAGAIN");
3653
3657
  await this.changes.wait(signal);
3654
3658
  }
@@ -3659,7 +3663,13 @@ var KernelSocket = class _KernelSocket {
3659
3663
  this.changes.wake();
3660
3664
  return true;
3661
3665
  }
3662
- connect(addr, viaUplink = true, cred, stamp) {
3666
+ get full() {
3667
+ return this.state === "listening" && this.queue.length >= this.backlog;
3668
+ }
3669
+ waitRoom(signal) {
3670
+ return this.room.wait(signal);
3671
+ }
3672
+ connect(addr, viaUplink = true, cred, stamp, waits = true) {
3663
3673
  if (this.state === "connected") throw new KernelError("EISCONN");
3664
3674
  if (this.state === "connecting") throw new KernelError("EALREADY");
3665
3675
  if (this.state !== "open") throw new KernelError("EINVAL");
@@ -3674,6 +3684,11 @@ var KernelSocket = class _KernelSocket {
3674
3684
  this.boundAt = this.local;
3675
3685
  } else this.local = { family: "unix", path: "" };
3676
3686
  }
3687
+ if (waits && target.full) return this.queued(addr, target, cred, stamp);
3688
+ this.join(addr, target, cred, stamp);
3689
+ return void 0;
3690
+ }
3691
+ join(addr, target, cred, stamp) {
3677
3692
  const server = new _KernelSocket(this.net, this.domain);
3678
3693
  server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
3679
3694
  server.peer = this.local;
@@ -3687,7 +3702,34 @@ var KernelSocket = class _KernelSocket {
3687
3702
  server.close();
3688
3703
  throw new KernelError("ECONNREFUSED");
3689
3704
  }
3690
- return void 0;
3705
+ }
3706
+ async queued(addr, target, cred, stamp) {
3707
+ this.state = "connecting";
3708
+ const abort = new AbortController();
3709
+ this.dialing = abort;
3710
+ const timer = setTimeout(
3711
+ () => abort.abort(new KernelError("ETIMEDOUT")),
3712
+ this.net.backlogWaitMs
3713
+ );
3714
+ try {
3715
+ while (target.full) await target.waitRoom(abort.signal);
3716
+ if (abort.signal.aborted) throw abort.signal.reason;
3717
+ this.state = "open";
3718
+ this.join(addr, target, cred, stamp);
3719
+ this.dialing = void 0;
3720
+ } catch (e) {
3721
+ const reason = abort.signal.aborted ? abort.signal.reason : e;
3722
+ const error = reason instanceof KernelError ? reason : new KernelError("ECONNREFUSED");
3723
+ if (this.dialing === abort) {
3724
+ this.dialing = void 0;
3725
+ this.failed = error.code;
3726
+ if (this.state === "connecting") this.state = "open";
3727
+ this.changes.wake();
3728
+ }
3729
+ throw error;
3730
+ } finally {
3731
+ clearTimeout(timer);
3732
+ }
3691
3733
  }
3692
3734
  dial(addr, route) {
3693
3735
  this.state = "connecting";
@@ -3788,6 +3830,7 @@ var KernelSocket = class _KernelSocket {
3788
3830
  this.dialing = void 0;
3789
3831
  if (was === "listening") {
3790
3832
  for (const pending of this.queue.splice(0)) pending.close();
3833
+ this.room.wake();
3791
3834
  }
3792
3835
  if (this.link) {
3793
3836
  if (!this.readShut) this.link.rx.closeRead();
@@ -3885,9 +3928,10 @@ var LoopbackNet = class {
3885
3928
  socket.listen(backlog);
3886
3929
  return socket;
3887
3930
  }
3931
+ backlogWaitMs = DIAL_TIMEOUT_MS;
3888
3932
  connect(addr) {
3889
3933
  const socket = this.socket(addr.family);
3890
- void socket.connect(addr, false);
3934
+ void socket.connect(addr, false, void 0, void 0, false);
3891
3935
  return socket;
3892
3936
  }
3893
3937
  bind(socket, addr, kernel = false) {
@@ -12140,7 +12184,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
12140
12184
  var EARLY_WHOLE_FILE = 64 * 1024;
12141
12185
 
12142
12186
  // src/version.ts
12143
- var KERNEL_VERSION = true ? "1.102.4" : "0.0.0-dev";
12187
+ var KERNEL_VERSION = true ? "1.102.5" : "0.0.0-dev";
12144
12188
  var OS_RELEASE = "6.0.0-slicc";
12145
12189
  function procVersion() {
12146
12190
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -13395,7 +13439,8 @@ var KernelLoopback = class {
13395
13439
  { family: "inet", host: "127.0.0.1", port },
13396
13440
  false,
13397
13441
  this.cred(),
13398
- this.stamp ? await this.stamp() : void 0
13442
+ this.stamp ? await this.stamp() : void 0,
13443
+ false
13399
13444
  );
13400
13445
  } catch (err) {
13401
13446
  socket.close();