@ai-ecoverse/slicc-kernel 1.99.1 → 1.100.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 +46 -1
- package/dist/kernel-worker.js +23 -13
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +152 -35
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +23 -13
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +118 -1
- package/dist/process-worker.js.map +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -568,7 +568,13 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
|
|
|
568
568
|
- **Caching:** the kernel caches attributes and listings for those TTLs, and a driver can push `invalidate` for changes made outside.
|
|
569
569
|
- **File contents:** on a mount with `ranges`, as `tmpfs` is, and on the root file system when it is in OPFS (in a browser with sync access handles) or an in-memory root, a program reads and writes a file in 64 KiB pages: reads come from the driver 1 MiB at a time, and changed pages go back in 1 MiB runs once 8 MiB have changed, at `fsync`, and when the file is closed (from the kernel's own descriptors also 250 ms after a write). A file of any size the mount can hold works, with memory to spare for about one more copy of it. In OPFS a write opens a sync access handle for just that write. An `fsa` mount (a picked folder) has no sync access handles, and Chrome copies the whole file into a swap file for every writable stream, so it writes through one stream per open file: the first write back opens it, later runs go into it at their offsets, and it is committed, one copy of the file, when the last process writing the file closes it (or exits), before another process reads what was written, and before a rename, unlink or unmount. Until then other processes see the new size but read the old bytes outside what was written. Elsewhere (`hostfs` with an older proxy, and a root outside OPFS), file contents are read whole when a program opens a file and written back when it closes it. Before a program's next call into the kernel (any wait, read of a pipe or terminal, sleep or spawn), its changed pages also go back, so other processes and the embedder see a write as soon as the writer does anything but compute: a daemon's pidfile, a log another process follows. On a whole-file mount that holds only for files of up to 64 KiB; a larger one goes back at close, fsync and exit, as before. Beyond that, both give close-to-open consistency, as NFS has. A process that ends without closing its files keeps what it wrote: on exit, on a trap, and when a signal kills it. For a killed WASI program, the kernel lets its worker write its changed pages back before `waitpid` reports the death. The only exception is a worker that makes no call into the kernel for 2 seconds after the signal; it is stopped without that write-back.
|
|
570
570
|
- **One file, several processes** ([#665](https://github.com/ai-ecoverse/slicc-kernel/issues/665)): a process that keeps a file open sees what another process writes to it. Every change the kernel applies to a file (a write back, a truncate, an append, a rename or a removal) bumps a change ring the kernel shares with all its processes. Before each read, write or seek to the end, a process's open file checks the ring (one atomic load) and, if its file changed, drops its clean pages and reads the size again, keeping its own unwritten changes. A write by one process is visible to another one's next read once it has gone back to the kernel: at the latest at the writer's next `fsync`, close, exit or call into the kernel (see above), so sqlite, which `fsync`s every commit, sees another connection's commit at its next transaction. A write through a file the kernel holds (a JS program's `ctx.open`, a WASI program's file after a fork or spawn) is in the kernel already, so other processes see it at their next read, before it is written back. A file the kernel itself holds open (a JS program's file, a WASI program's file after a fork or spawn) is the one copy: reads, writes, truncates and appends by path from other processes go to it, and their writes go through to storage at once, so a commit is in storage (and in `kernel.readFile`) when the committing process returns from `fsync`. `O_APPEND` writes go to the kernel at once, and the kernel picks the offset, so appenders in several processes keep every line. A file system without ranged I/O (`hostfs` with an older proxy, a root outside OPFS) rereads a file another process changed, but writes it back whole, so two writers there still need locks. The same file under two names is one file too ([#669](https://github.com/ai-ecoverse/slicc-kernel/issues/669)): a write by path through a symlink reaches the file the kernel holds under its real path (resolved without following links in a `sudo` process's no-links view, so an untrusted link is still refused), a WASI program's open file also watches its real path (resolved once, the first time another file changes), and an open file with more than one hard link checks its size, version and mtime when anything changed, so a write through another name is seen. Limits for now: an alias on a mount that has no `realpath` is still only the name it was opened by; another tab's writes reach this tab's processes only as far as the root's change notifications (OPFS's `BroadcastChannel` watch) carry them.
|
|
571
|
-
- **Locks between processes** ([#665](https://github.com/ai-ecoverse/slicc-kernel/issues/665)): an Emscripten program's `fcntl` byte-range locks (`F_SETLK`, `F_SETLKW`, `F_GETLK`) are the kernel's, so sqlite3 connections in different processes exclude each other: a second writer gets `database is locked` while the first holds its transaction, or waits with a busy timeout. The kernel keeps one table keyed by file identity (device and inode, so every name of a hard link is the same file), with POSIX semantics: locks belong to the process (an `exec`'d program keeps them under its pid; a forked child doesn't get them), a conflicting `F_SETLK` fails with `EAGAIN`, `F_GETLK` reports the holder's pid and range, `F_SETLKW` waits in the kernel without holding up anything else and a signal ends the wait with `EINTR`, a wait that would close a cycle between waiting processes fails with `EDEADLK`, a write lock on a descriptor not open for writing (or a read lock on one not open for reading) is `EBADF`, closing any descriptor of the file drops the process's locks on it, and exit releases them all. Before an unlock, the process writes back what it changed, so the next holder reads it. An open file's directory entry is checked against the change ring too: a name another process removed or replaced is looked up again, so sqlite's check for a hot journal sees the other connection's commit. A WASI imports module's `tryLock`/`unlock` keeps its behaviour (whole file, per process, keyed by path). Limits: locks are per kernel, not across tabs ([#675](https://github.com/ai-ecoverse/slicc-kernel/issues/675)); and a WASIX program
|
|
571
|
+
- **Locks between processes** ([#665](https://github.com/ai-ecoverse/slicc-kernel/issues/665)): an Emscripten program's `fcntl` byte-range locks (`F_SETLK`, `F_SETLKW`, `F_GETLK`) are the kernel's, and so are a WASIX program's through the `slicc.fcntl_lock` import (below), so sqlite3 connections in different processes exclude each other: a second writer gets `database is locked` while the first holds its transaction, or waits with a busy timeout. The kernel keeps one table keyed by file identity (device and inode, so every name of a hard link is the same file), with POSIX semantics: locks belong to the process (an `exec`'d program keeps them under its pid; a forked child doesn't get them), a conflicting `F_SETLK` fails with `EAGAIN`, `F_GETLK` reports the holder's pid and range, `F_SETLKW` waits in the kernel without holding up anything else and a signal ends the wait with `EINTR`, a wait that would close a cycle between waiting processes fails with `EDEADLK`, a write lock on a descriptor not open for writing (or a read lock on one not open for reading) is `EBADF`, closing any descriptor of the file drops the process's locks on it, and exit releases them all. Before an unlock, the process writes back what it changed, so the next holder reads it. An open file's directory entry is checked against the change ring too: a name another process removed or replaced is looked up again, so sqlite's check for a hot journal sees the other connection's commit. A WASI imports module's `tryLock`/`unlock` keeps its behaviour (whole file, per process, keyed by path). Limits: locks are per kernel, not across tabs ([#675](https://github.com/ai-ecoverse/slicc-kernel/issues/675)); and a WASIX program built against a wasix-sysroot without the import keeps its libc's local answer (every lock granted), so python's sqlite excludes other processes only once wasix-python is rebuilt on the sysroot that has it ([homescoop#407](https://github.com/ai-ecoverse/homescoop/issues/407)). An Emscripten program's `flock()` is a stub compiled into its libc that never reaches the kernel either, until a homescoop shim routes it to a kernel import ([#680](https://github.com/ai-ecoverse/slicc-kernel/issues/680)). A lock belongs to the file its descriptor was opened on: a file renamed over the name later, or the name's removal, doesn't move it.
|
|
572
|
+
- **`slicc.fcntl_lock(fd, cmd, flock_ptr) -> errno`** ([#722](https://github.com/ai-ecoverse/slicc-kernel/issues/722)): a WASIX program's `fcntl` record locks reach the same table as an Emscripten program's, so the two see each other's locks.
|
|
573
|
+
- **cmd:** `12` `F_GETLK`, `13` `F_SETLK` or `14` `F_SETLKW`. Any other cmd is `EINVAL`.
|
|
574
|
+
- **The struct:** musl's wasm32 `struct flock`, 32 bytes: `l_type` i16 at 0 (`0` read, `1` write, `2` unlock), `l_whence` i16 at 2 (`0` SET, `1` CUR, `2` END, resolved against the descriptor's offset or size), `l_start` i64 at 8, `l_len` i64 at 16 (`0` to EOF and beyond, negative before `l_start`), `l_pid` i32 at 24. The kernel reads and writes only those five fields and never the padding.
|
|
575
|
+
- **F_GETLK:** with a conflicting lock, it writes that lock back (`l_whence` 0, its absolute start, its length, the holder's pid). Without one, it sets only `l_type` to `2`.
|
|
576
|
+
- **Errors (WASI numbers):** `EAGAIN` for a conflict on `F_SETLK` (never `EACCES`), `EINTR` when a signal with a handler ends an `F_SETLKW` wait, `EDEADLK` for a wait that would close a cycle, `EBADF` for a bad descriptor or a lock type the descriptor's access mode doesn't allow, and `EINVAL` for a bad `l_type` or `l_whence` or a start before 0.
|
|
577
|
+
- **Semantics:** the same as Emscripten's, above. A libc that finds no import on an older kernel gets `ENOSYS` and keeps its local answer.
|
|
572
578
|
- **Rules on a mount:**
|
|
573
579
|
- a rename across mounts is `EXDEV`, so `mv` copies;
|
|
574
580
|
- a symlink on the root file system that leads onto a mount, such as `~/proj` → `/mnt/drive/proj`, or a chain from a mount to the root and back, is followed onto the mount by every operation, in every runtime: reads, writes, `stat`, listings, `mkdir`, removals, renames, `chmod`, `chown` and times. Removing the link itself removes the link. Before [#547](https://github.com/ai-ecoverse/slicc-kernel/issues/547) was fixed, the kernel resolved such a link against the directory under the mount point for JS and WASI programs, the embedder's `fs` and attached clients (Emscripten programs weren't affected): reads failed with `ENOENT`, and writes went into that hidden directory, which shows again once the mount is gone;
|
|
@@ -885,6 +891,35 @@ The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc
|
|
|
885
891
|
|
|
886
892
|
Releases are cut by semantic-release on every push to `main`, in two jobs, and only on `main`. The first job runs on the bench pool, so it starts within a minute of the push, before `main` has moved on. It computes the version, tags the commit and creates the GitHub release, with `npmPublish: false`. A run that starts after `main` has moved skips; the run for the newer commit releases it. The second job runs on a GitHub-hosted runner, because npm's trusted publishing and provenance accept no self-hosted runner. It publishes the tagged commit with `tools/release-recover.mjs`. However long it waits for a runner, it publishes exactly the commit the run started on, so the provenance, the tag and the package's `gitHead` agree. For any version tagged at HEAD, `tools/release-recover.mjs` pushes the tag if origin lacks it, publishes the version if npm lacks it (with retries), and creates its GitHub release if that is missing too. So a failure after the version is tagged (a rejected push, a failed `npm publish`) doesn't strand it, and re-running the Release workflow completes it the same way.
|
|
887
893
|
|
|
894
|
+
### Certification (`cert-review`)
|
|
895
|
+
|
|
896
|
+
`.github/workflows/cert-review.yml` certifies every release from CI. It runs when the Release workflow succeeds on `main`. The version is the tag on the released commit. The delta is read from the newest earlier release whose `cert-review` status is `success` or `pending`, skipping failed and unreviewed ones (or `base.json`'s release, or the tag before), so a release whose review was cancelled or failed is covered by the next one, and the line says `covers <first>–<last>`. It also runs on `workflow_dispatch` with a `version`, optionally with `prev` and `base`, or with `planted` (a branch) and `pr` for proofs. A dispatch off `main`, or a planted one, is a dry run. Each release (its commit) has its own concurrency group, so no pending review is cancelled by a newer one. The tools are in `tools/cert-review/`:
|
|
897
|
+
|
|
898
|
+
- **plan:** works out the version, the previous tag, the commit and the base (`base.mjs`). The base is the newest earlier release whose `cert-review` commit status is `success`, or, until there is one, the release `base.json` names (the cert lane's last line). CI never reads the npm `certified` dist-tag.
|
|
899
|
+
- **delta** (`delta.mjs`): lists the pull requests merged between the two tags (their merge commits, plus `pr` for a planted branch). Each gets its conventional-commit kind (`fix`, `feat`, `perf` and `revert` are behaviour changes) and the integration test files and titles it adds or changes. The titles are read from the pull request's patch: the added `test(…)` lines and the hunk headers git puts above changed lines. Escapes are decoded as JavaScript does, and a title git cut short, or a template literal, matches as a prefix.
|
|
900
|
+
- **suite** (`suite.mjs`, `reporter.mjs`) runs on the bench pool, without secrets. It takes the tag's tree with its own dependencies and Chromium, and replaces `dist/` with the **published npm tarball** (for a planted branch, its own `npm pack`).
|
|
901
|
+
- The release job runs the release's whole `test/integration/` suite, then the delta's test files again on the **previous release's dist** (the NEGATIVE).
|
|
902
|
+
- The base job runs the base release's own suite on its own published dist.
|
|
903
|
+
- Each run uses the tree's own `npm test` command, with a reporter that records every test and subtest with its status and error.
|
|
904
|
+
- **kernel-compat** (`compat.mjs`) supplies the base-set and catalog evidence: homescoop's kernel-compat run for the release.
|
|
905
|
+
- Release runs share one group and intermediate ones are cancelled. When there is no finished run for the release commit, it dispatches `kernel-compat.yml` for that version (whose run is named `Kernel compat v<version>`) and waits up to 150 minutes.
|
|
906
|
+
- It reads the run's report artifact, and **re-runs every regressing package once** (a dispatch with that `set`).
|
|
907
|
+
- A regression that doesn't reproduce is a `compat-flake`; one that does is a `compat-regression`. A crash that reproduces and doesn't happen on the reference kernel is a `compat-crash`, which record FAILs in code.
|
|
908
|
+
- **review:** `findings.mjs` decides in code, per pull request:
|
|
909
|
+
- `negative` (a fix's test fails on the previous dist and passes on the release) or `passes` (a feature's test passes);
|
|
910
|
+
- otherwise a finding: `no-test`, `no-negative`, `fails-on-release`, `not-run`, `prev-not-run` or `no-titles`.
|
|
911
|
+
|
|
912
|
+
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. Claude, on Bedrock with the key only in that step, then reads those artifacts and `instructions.txt` (which has the roll-forward FAIL list verbatim) and writes `verdict.json` and `comment.md`. Its tools are reading files, `gh pr view`, and `gh issue list`/`view`. `gh issue create` is allowed only for a release on `main`; otherwise, and for an issue in another repository, it lists the issue under `elsewhere` for the cert lane to file.
|
|
913
|
+
- **record** (`record.mjs`), in code, with read-only permissions:
|
|
914
|
+
- It re-downloads the tarball and checks `dist.integrity` (sha512) and the sha256, the SLSA v1 provenance (the tarball is its subject; `release.yml`, `refs/heads/main`, a GitHub-hosted builder; commit = `gitHead` = the tag), `npm audit signatures`, and that the commit is on `main`. A problem there, a `compat-crash`, or a planted build is a FAIL whatever the review says.
|
|
915
|
+
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
916
|
+
- The line is `certified @ai-ecoverse/slicc-kernel@<v> sha256=<…> (ci: <run>; known: …)`. Any test gaps from the findings (`no-test`, `no-negative`, `prev-not-run`, `no-titles`, `not-run`) are appended in code as `test gaps: #<pr> <kind>`, so a fix without a NEGATIVE is never certified silently.
|
|
917
|
+
- **publish** runs only for a release on `main`, never for a dry run. It puts the line into a `cert-review` commit status on the tag's commit and into the GitHub release's notes, between `<!-- cert-review -->` markers.
|
|
918
|
+
|
|
919
|
+
Moving the `certified` dist-tag stays manual. Automating it needs an npm token that may change dist-tags, in a step that re-hashes the tarball and tags only on a `certified` line.
|
|
920
|
+
|
|
921
|
+
Not part of the run yet: bios's real setup (the base-set sweep in bios, the upgrade boot, the sudo rows under `enforce`). It would plug in as one more job before review that runs slicc-bios's `npm run test:cert` with `CERT_COMMANDS` and the release's tarball, and leaves its results next to the others.
|
|
922
|
+
|
|
888
923
|
## Test fixtures
|
|
889
924
|
|
|
890
925
|
The WASIX fixtures of `test/integration/epoll.test.mjs` (#674), under `test/integration/fixtures/`, are committed with their sources, build scripts and built wasm.
|
|
@@ -976,6 +1011,16 @@ A WASIX C program for `test/integration/semaphore.test.mjs` (#717). It calls the
|
|
|
976
1011
|
|
|
977
1012
|
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 (legacy exceptions, `-pthread`) and binaryen's `wasm-opt` (version 132, `--asyncify` for `proc_fork`) on `PATH`. `bin/semtest.wasm` is 59,187 bytes, sha256 `c2091c18d799881721bb8e0e2fc7269732d52d6cd545f4f57266e8e8864c9ee6`.
|
|
978
1013
|
|
|
1014
|
+
### `wlocktest`
|
|
1015
|
+
|
|
1016
|
+
A WASIX C program for `test/integration/wasix-locks.test.mjs` (#722). It calls `slicc.fcntl_lock` directly (`import_module("slicc")`), so the rows don't wait for the wasix-sysroot side. Its arguments are a sequence of steps, run in order:
|
|
1017
|
+
- `open SLOT r|w|rw PATH`, `seek SLOT OFFSET`, `close SLOT`;
|
|
1018
|
+
- `set|setw|get SLOT TYPE WHENCE START LEN`, which call `F_SETLK`, `F_SETLKW` or `F_GETLK` on a struct pre-filled with `0x5a`. `get` reports the holder, or whether every byte but `l_type` stayed as it was;
|
|
1019
|
+
- `cmd SLOT N`, `badfd`;
|
|
1020
|
+
- `touch PATH`, `await PATH`, `sleep MS`, `pid`, `trap` (a `SIGUSR1` handler), `fork SLOT` (the child runs `F_GETLK` and `F_SETLK` on the slot).
|
|
1021
|
+
|
|
1022
|
+
**Build:** `INSTALL_WASIXCC=<homescoop>/scripts/install-wasixcc.sh WASIX_SYSROOT_28=<unpacked @ai-ecoverse/wasix-sysroot 2025.9.30-28> ./build.sh`, with homescoop's pinned wasixcc 0.4.7 (LLVM 21.1.206, binaryen 133) and Homebrew's binaryen `wasm-opt` 132 on `PATH` for the Asyncify pass (`proc_fork`). Two builds give the same bytes: `bin/wlocktest.wasm` is 56,958 bytes, sha256 `31308a54e13e7dcac497ad9d8a05799008c8beaa4ae3a36c76e218af7235ec23`.
|
|
1023
|
+
|
|
979
1024
|
## License
|
|
980
1025
|
|
|
981
1026
|
Apache-2.0
|
package/dist/kernel-worker.js
CHANGED
|
@@ -9305,12 +9305,18 @@ var DriverConnection = class _DriverConnection {
|
|
|
9305
9305
|
timeoutMs;
|
|
9306
9306
|
nextId = 0;
|
|
9307
9307
|
onFail;
|
|
9308
|
-
|
|
9308
|
+
idle;
|
|
9309
|
+
constructor(port, capabilities, waiting, timeoutMs, options) {
|
|
9309
9310
|
this.port = port;
|
|
9310
9311
|
this.capabilities = capabilities;
|
|
9311
9312
|
this.waiting = waiting;
|
|
9312
9313
|
this.timeoutMs = timeoutMs;
|
|
9313
|
-
this.onFail = onFail;
|
|
9314
|
+
this.onFail = options.onFail;
|
|
9315
|
+
this.idle = options.idle === true;
|
|
9316
|
+
if (this.idle) port.unref?.();
|
|
9317
|
+
}
|
|
9318
|
+
settled() {
|
|
9319
|
+
if (this.idle && this.waiting.size === 0) this.port.unref?.();
|
|
9314
9320
|
}
|
|
9315
9321
|
static async open(port, mount, options = {}) {
|
|
9316
9322
|
const timeoutMs = options.timeoutMs ?? DRIVER_TIMEOUT_MS;
|
|
@@ -9326,6 +9332,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9326
9332
|
clearTimeout(call.timer);
|
|
9327
9333
|
if (data.errno) call.reject(errnoError(data.errno, data.message ?? data.errno));
|
|
9328
9334
|
else call.resolve(data.result);
|
|
9335
|
+
if (options.idle && waiting.size === 0) port.unref?.();
|
|
9329
9336
|
});
|
|
9330
9337
|
port.start?.();
|
|
9331
9338
|
port.postMessage({ hello: { protocol: DRIVER_PROTOCOL, mount } });
|
|
@@ -9344,7 +9351,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9344
9351
|
const refused2 = driverVersionError(hello.protocol);
|
|
9345
9352
|
if (refused2) throw errnoError("EPROTO", refused2);
|
|
9346
9353
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
9347
|
-
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options
|
|
9354
|
+
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
9348
9355
|
}
|
|
9349
9356
|
call(call, transfer = []) {
|
|
9350
9357
|
if (this.failure) return Promise.reject(this.failure);
|
|
@@ -9353,6 +9360,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9353
9360
|
const timer = unref(
|
|
9354
9361
|
setTimeout(() => {
|
|
9355
9362
|
this.waiting.delete(id2);
|
|
9363
|
+
this.settled();
|
|
9356
9364
|
const error = errnoError(
|
|
9357
9365
|
"EIO",
|
|
9358
9366
|
`the driver did not answer ${call.op} within ${this.timeoutMs} ms`
|
|
@@ -9362,6 +9370,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9362
9370
|
this.onFail?.(error);
|
|
9363
9371
|
}, this.timeoutMs)
|
|
9364
9372
|
);
|
|
9373
|
+
if (this.idle && this.waiting.size === 0) this.port.ref?.();
|
|
9365
9374
|
this.waiting.set(id2, { resolve: resolve2, reject, timer });
|
|
9366
9375
|
this.port.postMessage({ ...call, id: id2 }, transfer);
|
|
9367
9376
|
});
|
|
@@ -9374,6 +9383,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9374
9383
|
call.reject(error);
|
|
9375
9384
|
}
|
|
9376
9385
|
this.waiting.clear();
|
|
9386
|
+
this.settled();
|
|
9377
9387
|
}
|
|
9378
9388
|
close() {
|
|
9379
9389
|
this.fail(errnoError("EIO", "the file system is unmounted"));
|
|
@@ -9728,6 +9738,7 @@ var MountTable = class {
|
|
|
9728
9738
|
{ source: spec.source, options },
|
|
9729
9739
|
{
|
|
9730
9740
|
...this.deps.timeoutMs ? { timeoutMs: this.deps.timeoutMs } : {},
|
|
9741
|
+
...opened2.idle ? { idle: true } : {},
|
|
9731
9742
|
onInvalidate: (paths) => this.invalidated(target, paths),
|
|
9732
9743
|
onFail: (error) => {
|
|
9733
9744
|
if (mounted) this.crashed(mounted, error);
|
|
@@ -12096,7 +12107,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
|
|
|
12096
12107
|
var EARLY_WHOLE_FILE = 64 * 1024;
|
|
12097
12108
|
|
|
12098
12109
|
// src/version.ts
|
|
12099
|
-
var KERNEL_VERSION = true ? "1.
|
|
12110
|
+
var KERNEL_VERSION = true ? "1.100.0" : "0.0.0-dev";
|
|
12100
12111
|
var OS_RELEASE = "6.0.0-slicc";
|
|
12101
12112
|
function procVersion() {
|
|
12102
12113
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -13136,7 +13147,7 @@ async function recordLock2(req, ctx) {
|
|
|
13136
13147
|
return done2;
|
|
13137
13148
|
}
|
|
13138
13149
|
const range = lockRange(req.start, req.len);
|
|
13139
|
-
const key = await fileKey(target, req.kind);
|
|
13150
|
+
const key = await fileKey(target, req.kind, req.op === "lk-set" && req.fd !== void 0);
|
|
13140
13151
|
if (req.op === "lk-get") {
|
|
13141
13152
|
const held = locks.holder(key, owner, { key, kind: req.kind, ...range });
|
|
13142
13153
|
if (!held) return json3(null);
|
|
@@ -15956,7 +15967,7 @@ var WasmProcess = class {
|
|
|
15956
15967
|
return await hostSyscall(req, {
|
|
15957
15968
|
pid: this.pid,
|
|
15958
15969
|
owner: () => this.lockOwner(),
|
|
15959
|
-
fileKey: (target, kind) => this.lockKey(target, kind),
|
|
15970
|
+
fileKey: (target, kind, setting) => this.lockKey(target, kind, setting),
|
|
15960
15971
|
locks: this.options.locks,
|
|
15961
15972
|
ops: this.asyncOps,
|
|
15962
15973
|
blocking: () => this.blockingSignal(cancelled),
|
|
@@ -16008,8 +16019,11 @@ var WasmProcess = class {
|
|
|
16008
16019
|
}
|
|
16009
16020
|
return path;
|
|
16010
16021
|
}
|
|
16011
|
-
async lockKey(target, kind) {
|
|
16022
|
+
async lockKey(target, kind, setting = false) {
|
|
16012
16023
|
const file = target.fd === void 0 ? void 0 : this.fds.get(target.fd);
|
|
16024
|
+
const access2 = file?.file;
|
|
16025
|
+
if (setting && (kind === "read" && !access2?.read || kind === "write" && !access2?.write))
|
|
16026
|
+
throw new KernelError("EBADF");
|
|
16013
16027
|
const known = file ? fileKeys.get(file) : this.pathKeys.get(target.path ?? "");
|
|
16014
16028
|
if (kind === "close" && !file && target.path !== void 0) this.pathKeys.delete(target.path);
|
|
16015
16029
|
const want = kind === "read" ? R_OK : kind === "write" ? W_OK : 0;
|
|
@@ -20558,9 +20572,6 @@ var NOT_EXECUTABLE = 126;
|
|
|
20558
20572
|
var DECIDE_MS = 100;
|
|
20559
20573
|
var INIT_PID = 1;
|
|
20560
20574
|
var SHARED_DIRS = ["/tmp", "/home"];
|
|
20561
|
-
function unref2(port) {
|
|
20562
|
-
port.unref?.();
|
|
20563
|
-
}
|
|
20564
20575
|
var LAYOUT = [
|
|
20565
20576
|
["/", 493],
|
|
20566
20577
|
["/root", 448],
|
|
@@ -20771,7 +20782,6 @@ var Launcher = class {
|
|
|
20771
20782
|
pnpmMade;
|
|
20772
20783
|
pnpmEpoch = 0;
|
|
20773
20784
|
mounts;
|
|
20774
|
-
shmPorts = [];
|
|
20775
20785
|
createDriverWorker;
|
|
20776
20786
|
media;
|
|
20777
20787
|
onMountPending;
|
|
@@ -22036,9 +22046,10 @@ var Launcher = class {
|
|
|
22036
22046
|
},
|
|
22037
22047
|
{ owned: true }
|
|
22038
22048
|
);
|
|
22039
|
-
if (spec.target === SHM)
|
|
22049
|
+
if (spec.target === SHM) port2.unref?.();
|
|
22040
22050
|
return {
|
|
22041
22051
|
port: port1,
|
|
22052
|
+
...spec.target === SHM ? { idle: true } : {},
|
|
22042
22053
|
dispose: () => {
|
|
22043
22054
|
port1.close();
|
|
22044
22055
|
port2.close();
|
|
@@ -22142,7 +22153,6 @@ var Launcher = class {
|
|
|
22142
22153
|
async mountShm() {
|
|
22143
22154
|
await this.base.mkdir(SHM, { recursive: true });
|
|
22144
22155
|
await this.mounts.mountSystem({ type: "tmpfs", source: "shm", target: SHM }, this.fs);
|
|
22145
|
-
for (const port of this.shmPorts) unref2(port);
|
|
22146
22156
|
await this.fs.chmod(SHM, 1023);
|
|
22147
22157
|
}
|
|
22148
22158
|
async ownTmp() {
|