@threadedstack/wmd-web 0.1.16 → 0.1.18
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 +48 -2
- package/RELEASE.json +41 -56
- package/guest/arm64/Image +2 -2
- package/guest/arm64/Image-4k +1 -1
- package/guest/arm64/initramfs.cpio.gz +0 -0
- package/guest/arm64/vm.wasm +0 -0
- package/package.json +4 -4
- package/relay/hosted.ts +10 -8
- package/relay/main.ts +2 -2
- package/relay/relay.ts +1 -0
- package/relay/token.ts +34 -5
- package/relay/wisp.ts +37 -5
- package/src/boot.js +53 -9
- package/src/build/builder.js +791 -0
- package/src/build/context.js +349 -0
- package/src/build/dockerfile.js +1055 -0
- package/src/build/dockerignore.js +270 -0
- package/src/build/fast.js +1187 -0
- package/src/build/inspect.js +78 -0
- package/src/build/pin.js +6 -0
- package/src/build/tar.js +304 -0
- package/src/host/ckpt-point.js +55 -10
- package/src/host/ckpt.js +35 -2
- package/src/host/ctl.js +17 -0
- package/src/host/disk-map.js +330 -0
- package/src/host/disk-worker.js +125 -0
- package/src/host/disk.js +219 -0
- package/src/host/wmd-imports.js +3 -3
- package/src/image-files.js +2 -1
- package/src/registry/hub.js +113 -44
- package/src/registry/load-tar.js +473 -0
- package/src/registry/pull.js +5 -3
- package/src/worker.js +5 -1
- package/wmdpack.wasm +0 -0
package/src/boot.js
CHANGED
|
@@ -51,9 +51,11 @@ import {
|
|
|
51
51
|
} from './host/ckpt.js'
|
|
52
52
|
import { captureJit, jitSnapStats, modulesOf, ownersNeedingHub, restoreStore } from './host/jit-snap.js'
|
|
53
53
|
export { CheckpointError, memoryStore, opfsStore } from './host/ckpt.js'
|
|
54
|
+
export { DiskError, deleteDisk, openDisk, sweepDisks } from './host/disk.js'
|
|
54
55
|
export { NetSwitch } from './net/switch.js'
|
|
55
56
|
export { ImageFiles, openImage, parseGroup, parsePasswd, resolveUser } from './image-files.js'
|
|
56
57
|
export { P9_CACHE_DEFAULT, p9InitCmdline, p9MountOptions } from './host/p9mount.js'
|
|
58
|
+
export { loadImageTar } from './registry/load-tar.js'
|
|
57
59
|
|
|
58
60
|
const PAGE = 65536
|
|
59
61
|
/** A trap with the memory this close to its maximum is reported as out of memory. */
|
|
@@ -414,12 +416,30 @@ function toShared(bytes) {
|
|
|
414
416
|
return sab
|
|
415
417
|
}
|
|
416
418
|
|
|
419
|
+
/** vm.wasm's arguments for a machine with `disk`: --swap <MiB> and wmd.disk=data appended to --append. */
|
|
420
|
+
export function diskArgs(args, disk) {
|
|
421
|
+
if (args.includes('--swap')) throw new TypeError('--swap and a disk: give one')
|
|
422
|
+
const mib = disk.bytes / 2 ** 20
|
|
423
|
+
if (!Number.isInteger(mib) || mib < 1) throw new TypeError(`disk size ${disk.bytes} is not whole MiB`)
|
|
424
|
+
const out = [...args, '--swap', String(mib)]
|
|
425
|
+
const i = out.indexOf('--append')
|
|
426
|
+
if (i < 0 || i + 1 >= out.length) throw new TypeError('a disk needs --append (the kernel line gets wmd.disk=data)')
|
|
427
|
+
// the disk holds data: a wmd.disk= of any other value would let the guest init write a swap header over it
|
|
428
|
+
const given = out[i + 1].match(/(?:^| )wmd\.disk(?:=(\S*))?(?= |$)/g)
|
|
429
|
+
if (given && given.some(g => g.trim() !== 'wmd.disk=data')) throw new TypeError(`a disk needs wmd.disk=data on the kernel line, not ${given.map(g => g.trim()).join(' ')}`)
|
|
430
|
+
if (!given) out[i + 1] = `${out[i + 1]} wmd.disk=data`
|
|
431
|
+
return out
|
|
432
|
+
}
|
|
433
|
+
|
|
417
434
|
/**
|
|
418
435
|
* @param {object} o
|
|
419
436
|
* @param {string|Uint8Array|ArrayBuffer} o.vmUrl URL of vm.wasm, or its bytes
|
|
420
437
|
* @param {Record<string,string|Uint8Array>} o.files preopen contents: path → URL or bytes
|
|
421
438
|
* @param {string[]} o.args vm.wasm arguments (after argv[0])
|
|
422
439
|
* @param {string[]} [o.env] "K=V" (the runner passes only WMD_*)
|
|
440
|
+
* @param {object} [o.disk] a disk from openDisk (host/disk.js): the guest gets it as virtio-blk
|
|
441
|
+
* /dev/vda (vm.wasm's --swap MiB sizes the device) with wmd.disk=data on the kernel line, so guest init leaves
|
|
442
|
+
* it alone. A machine with a disk takes no checkpoints: guest memory and the disk would not match
|
|
423
443
|
* @param {boolean} [o.heapReserve] W21 (default true, false in mobile WebKit: heapReserveDefault, D-516): vm.wasm grows its memory to the maximum
|
|
424
444
|
* before any thread starts (WMD_HEAP_RESERVE=1), so memory never grows under running
|
|
425
445
|
* vCPUs. Chromium checks memory.fill/copy against a per-Worker size that lags another
|
|
@@ -520,6 +540,10 @@ export async function bootWmd(o) {
|
|
|
520
540
|
o.onStderr?.(text)
|
|
521
541
|
}
|
|
522
542
|
const preopen = o.preopen || '/g'
|
|
543
|
+
if (o.disk) {
|
|
544
|
+
if (o.checkpoint || o.restore || o.restoreLazy) throw new TypeError('a machine with a disk takes no checkpoints')
|
|
545
|
+
o = { ...o, args: diskArgs(o.args, o.disk) }
|
|
546
|
+
}
|
|
523
547
|
const argv = ['vm.wasm', ...o.args]
|
|
524
548
|
const env = vmEnv(o.env, o.heapReserve ?? heapReserveDefault())
|
|
525
549
|
// the default Worker is started from a literal new URL(..., import.meta.url) so bundlers (Vite, webpack)
|
|
@@ -531,14 +555,12 @@ export async function bootWmd(o) {
|
|
|
531
555
|
const flatRam = wantsFlatRam(o.args, o)
|
|
532
556
|
if (!flatRam) ramMiB(o.args, o.allowLargeRam) // a bad or too big --ram is a clear error before anything is downloaded
|
|
533
557
|
const maximum = o.maxPages || (flatRam ? flatMaxPages(o.args) : memoryMaxPages(o.args, 1024, o.allowLargeRam))
|
|
534
|
-
if (flatRam)
|
|
535
|
-
if (o.restore || o.restoreLazy) throw new Error(`--ram ${ramMiB(o.args, true)}: a guest with more than ${MAX_BROWSER_RAM_MIB} MiB cannot start from a checkpoint yet`)
|
|
536
|
-
env.push('WMD_RAM_FLAT=1')
|
|
537
|
-
}
|
|
558
|
+
if (flatRam) env.push('WMD_RAM_FLAT=1')
|
|
538
559
|
const ramMem = flatRam ? createRamMemory(ramMiB(o.args, true)) : null
|
|
560
|
+
/** The RAM memory's pages (0: the RAM is in memory 0). A checkpoint of a flat guest also holds its RAM memory's pages (sections ram-index, ram). */
|
|
561
|
+
const ramPages = ramMem ? ramMem.buffer.byteLength / CKPT_PAGE : 0
|
|
539
562
|
// P-B9: checkpoints. restoreIn: the checkpoint's parts (parseCheckpoint refuses a malformed one).
|
|
540
|
-
|
|
541
|
-
const ckptOn = !flatRam && (!!o.checkpoint || !!o.restore || !!o.restoreLazy)
|
|
563
|
+
const ckptOn = !!o.checkpoint || !!o.restore || !!o.restoreLazy
|
|
542
564
|
let restoreIn = o.restore ? parseCheckpoint(o.restore) : null
|
|
543
565
|
// P-B6: a base snapshot that is still downloading ({header: Promise, ckpt: Promise, abort()}, src/base.js openBase()). The
|
|
544
566
|
// boot downloads its own files meanwhile; the snapshot's header is checked against this boot's vm.wasm and boot key as soon
|
|
@@ -740,7 +762,7 @@ export async function bootWmd(o) {
|
|
|
740
762
|
const got = typeof box.take === 'function' ? box.take() : box // a box hands the snapshot over once: the opener keeps no reference to its 0.5 GB
|
|
741
763
|
ckptT.baseWaitMs = absMs() - tw // the time the boot waited for the snapshot after everything else was in
|
|
742
764
|
const parsed = parseCheckpoint(got)
|
|
743
|
-
checkCheckpointFits(parsed.header, { vmKey, bootKey, bootDesc, maxPages: maximum, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
765
|
+
checkCheckpointFits(parsed.header, { vmKey, bootKey, bootDesc, maxPages: maximum, ramPages, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
744
766
|
checkPacksFit(parsed.header.packs, packDigests)
|
|
745
767
|
restoreIn = parsed
|
|
746
768
|
lazyIn = null // baseLazy stays: accept() when the restore has succeeded, reject() if it fails
|
|
@@ -749,7 +771,7 @@ export async function bootWmd(o) {
|
|
|
749
771
|
baseRefused(err)
|
|
750
772
|
}
|
|
751
773
|
}
|
|
752
|
-
if (restoreIn) checkCheckpointFits(restoreIn.header, { vmKey, bootKey, bootDesc, maxPages: maximum, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
774
|
+
if (restoreIn) checkCheckpointFits(restoreIn.header, { vmKey, bootKey, bootDesc, maxPages: maximum, ramPages, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
753
775
|
}
|
|
754
776
|
if (signal?.aborted) throw abortError()
|
|
755
777
|
} catch (err) {
|
|
@@ -812,6 +834,26 @@ export async function bootWmd(o) {
|
|
|
812
834
|
kept[p] = 1
|
|
813
835
|
}
|
|
814
836
|
}
|
|
837
|
+
// the flat layout: the guest's RAM, into its own memory (a fresh one: all zero but for these pages)
|
|
838
|
+
if (restoreIn.ram) {
|
|
839
|
+
const ram = ramMem.buffer
|
|
840
|
+
const win = 16384 // pages per view: a RAM memory can be past the length of one typed array view
|
|
841
|
+
let at = -1
|
|
842
|
+
let v = null
|
|
843
|
+
let r = 0
|
|
844
|
+
for (const c of restoreIn.ram.chunks) {
|
|
845
|
+
for (let off = 0; off < c.length; off += CKPT_PAGE) {
|
|
846
|
+
const p = restoreIn.ram.index[r++]
|
|
847
|
+
const lo = p - (p % win)
|
|
848
|
+
if (lo !== at) {
|
|
849
|
+
at = lo
|
|
850
|
+
v = new Uint8Array(ram, lo * CKPT_PAGE, Math.min(win, ramPages - lo) * CKPT_PAGE)
|
|
851
|
+
}
|
|
852
|
+
v.set(c.subarray(off, off + CKPT_PAGE), (p - lo) * CKPT_PAGE)
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
ckptT.ramPagesRestored = r
|
|
856
|
+
}
|
|
815
857
|
// pages the instance's start function wrote that were zero at the checkpoint (the runner's zero_absent): the static region
|
|
816
858
|
let zeroed = 0
|
|
817
859
|
const w = new Int32Array(memory.buffer, 0, Math.min(restoreIn.header.memPages, VM_MIN_PAGES) * (CKPT_PAGE / 4))
|
|
@@ -845,6 +887,7 @@ export async function bootWmd(o) {
|
|
|
845
887
|
be.now('restore', { t: 'boot', stage: 'restore', state: 'done', used: true, bytes: baseBytes, total: baseTotal, source: baseSource, tookMs: Math.round(ckptT.pagesWrittenAt) })
|
|
846
888
|
restoreIn.memChunks = null
|
|
847
889
|
restoreIn.index = null
|
|
890
|
+
restoreIn.ram = null
|
|
848
891
|
restoreIn.jit = null
|
|
849
892
|
restoreIn.svc = null
|
|
850
893
|
restoreIn.ctl = null
|
|
@@ -1531,6 +1574,7 @@ export async function bootWmd(o) {
|
|
|
1531
1574
|
tableMin,
|
|
1532
1575
|
memory,
|
|
1533
1576
|
ram: ramMem,
|
|
1577
|
+
blk: o.disk?.mailbox || null,
|
|
1534
1578
|
ctlSab,
|
|
1535
1579
|
files,
|
|
1536
1580
|
preopen,
|
|
@@ -1854,7 +1898,7 @@ export async function bootWmd(o) {
|
|
|
1854
1898
|
try {
|
|
1855
1899
|
// everything that can fail without harming the running machine comes first: the checkpoint parses and is this boot's
|
|
1856
1900
|
const parsed = parseCheckpoint(ckpt)
|
|
1857
|
-
checkCheckpointFits(parsed.header, { vmKey, bootKey, bootDesc, maxPages: maximum, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
1901
|
+
checkCheckpointFits(parsed.header, { vmKey, bootKey, bootDesc, maxPages: maximum, ramPages, hasJit: o.jit !== false, fmaFused: fmaFuses() })
|
|
1858
1902
|
if (!heldAssets) throw new CheckpointError('not-ready', 'this machine was booted without checkpoint support: it has no kept boot files to restore with')
|
|
1859
1903
|
validateFdTable(parsed.ctl, heldAssets.files.length) // the fd table is checked against this boot's files now, not after the old machine is gone
|
|
1860
1904
|
// the boot's own bytes (vm.wasm and its compiled Module, the kernel, the packs, the keys) are reused: nothing is
|