@ecsia/scheduler 0.1.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/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/commands/apply.d.ts +49 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +211 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/buffer.d.ts +53 -0
- package/dist/commands/buffer.d.ts.map +1 -0
- package/dist/commands/buffer.js +66 -0
- package/dist/commands/buffer.js.map +1 -0
- package/dist/commands/encode.d.ts +30 -0
- package/dist/commands/encode.d.ts.map +1 -0
- package/dist/commands/encode.js +108 -0
- package/dist/commands/encode.js.map +1 -0
- package/dist/commands/fields.d.ts +22 -0
- package/dist/commands/fields.d.ts.map +1 -0
- package/dist/commands/fields.js +123 -0
- package/dist/commands/fields.js.map +1 -0
- package/dist/commands/index.d.ts +10 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/op.d.ts +16 -0
- package/dist/commands/op.d.ts.map +1 -0
- package/dist/commands/op.js +43 -0
- package/dist/commands/op.js.map +1 -0
- package/dist/executor/guards.d.ts +8 -0
- package/dist/executor/guards.d.ts.map +1 -0
- package/dist/executor/guards.js +61 -0
- package/dist/executor/guards.js.map +1 -0
- package/dist/executor/index.d.ts +9 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +6 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/run-wave.d.ts +19 -0
- package/dist/executor/run-wave.d.ts.map +1 -0
- package/dist/executor/run-wave.js +38 -0
- package/dist/executor/run-wave.js.map +1 -0
- package/dist/executor/scheduler.d.ts +41 -0
- package/dist/executor/scheduler.d.ts.map +1 -0
- package/dist/executor/scheduler.js +71 -0
- package/dist/executor/scheduler.js.map +1 -0
- package/dist/executor/seams.d.ts +38 -0
- package/dist/executor/seams.d.ts.map +1 -0
- package/dist/executor/seams.js +16 -0
- package/dist/executor/seams.js.map +1 -0
- package/dist/executor/update-threaded.d.ts +17 -0
- package/dist/executor/update-threaded.d.ts.map +1 -0
- package/dist/executor/update-threaded.js +67 -0
- package/dist/executor/update-threaded.js.map +1 -0
- package/dist/executor/update.d.ts +4 -0
- package/dist/executor/update.d.ts.map +1 -0
- package/dist/executor/update.js +23 -0
- package/dist/executor/update.js.map +1 -0
- package/dist/graph/dag.d.ts +16 -0
- package/dist/graph/dag.d.ts.map +1 -0
- package/dist/graph/dag.js +118 -0
- package/dist/graph/dag.js.map +1 -0
- package/dist/graph/edges.d.ts +20 -0
- package/dist/graph/edges.d.ts.map +1 -0
- package/dist/graph/edges.js +181 -0
- package/dist/graph/edges.js.map +1 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +5 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/waves.d.ts +30 -0
- package/dist/graph/waves.d.ts.map +1 -0
- package/dist/graph/waves.js +133 -0
- package/dist/graph/waves.js.map +1 -0
- package/dist/graph/weights.d.ts +7 -0
- package/dist/graph/weights.d.ts.map +1 -0
- package/dist/graph/weights.js +10 -0
- package/dist/graph/weights.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +13 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +12 -0
- package/dist/internal.js.map +1 -0
- package/dist/planner/access.d.ts +18 -0
- package/dist/planner/access.d.ts.map +1 -0
- package/dist/planner/access.js +92 -0
- package/dist/planner/access.js.map +1 -0
- package/dist/planner/define-system.d.ts +14 -0
- package/dist/planner/define-system.d.ts.map +1 -0
- package/dist/planner/define-system.js +30 -0
- package/dist/planner/define-system.js.map +1 -0
- package/dist/planner/index.d.ts +6 -0
- package/dist/planner/index.d.ts.map +1 -0
- package/dist/planner/index.js +4 -0
- package/dist/planner/index.js.map +1 -0
- package/dist/planner/types.d.ts +74 -0
- package/dist/planner/types.d.ts.map +1 -0
- package/dist/planner/types.js +6 -0
- package/dist/planner/types.js.map +1 -0
- package/dist/workers/atomics-shim.d.ts +8 -0
- package/dist/workers/atomics-shim.d.ts.map +1 -0
- package/dist/workers/atomics-shim.js +14 -0
- package/dist/workers/atomics-shim.js.map +1 -0
- package/dist/workers/index.d.ts +13 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +10 -0
- package/dist/workers/index.js.map +1 -0
- package/dist/workers/manifest.d.ts +67 -0
- package/dist/workers/manifest.d.ts.map +1 -0
- package/dist/workers/manifest.js +5 -0
- package/dist/workers/manifest.js.map +1 -0
- package/dist/workers/pool.d.ts +60 -0
- package/dist/workers/pool.d.ts.map +1 -0
- package/dist/workers/pool.js +313 -0
- package/dist/workers/pool.js.map +1 -0
- package/dist/workers/reservation.d.ts +18 -0
- package/dist/workers/reservation.d.ts.map +1 -0
- package/dist/workers/reservation.js +41 -0
- package/dist/workers/reservation.js.map +1 -0
- package/dist/workers/wave-sync.d.ts +18 -0
- package/dist/workers/wave-sync.d.ts.map +1 -0
- package/dist/workers/wave-sync.js +88 -0
- package/dist/workers/wave-sync.js.map +1 -0
- package/dist/workers/worker-entry.d.ts +2 -0
- package/dist/workers/worker-entry.d.ts.map +1 -0
- package/dist/workers/worker-entry.js +187 -0
- package/dist/workers/worker-entry.js.map +1 -0
- package/dist/workers/worker-system.d.ts +31 -0
- package/dist/workers/worker-system.d.ts.map +1 -0
- package/dist/workers/worker-system.js +20 -0
- package/dist/workers/worker-system.js.map +1 -0
- package/dist/workers/world-view.d.ts +39 -0
- package/dist/workers/world-view.d.ts.map +1 -0
- package/dist/workers/world-view.js +85 -0
- package/dist/workers/world-view.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// The Atomics wave-completion fence and the three-tier wait. The main
|
|
2
|
+
// thread dispatches a round's batches, then waits until every worker has decremented the SAB counter
|
|
3
|
+
// to zero. The tier is chosen ONCE at world creation by the capability probe (selectWaitTier, seams.ts).
|
|
4
|
+
//
|
|
5
|
+
// WaveCounter words: [0]=remaining [1]=epoch [2]=errorFlag [3]=padding.
|
|
6
|
+
import { waitAsync } from './atomics-shim.js';
|
|
7
|
+
const REMAINING = 0;
|
|
8
|
+
const EPOCH = 1;
|
|
9
|
+
const ERROR = 2;
|
|
10
|
+
/**
|
|
11
|
+
* Words [0..3] are the control block (remaining, epoch, errorFlag, padding); words [4..4+workers)
|
|
12
|
+
* are the per-worker buffer `head` each worker stores before completing, so the main thread reads the
|
|
13
|
+
* record count after the fence without any postMessage on the hot path (tier-2 blocking-main path).
|
|
14
|
+
*/
|
|
15
|
+
export function makeWaveCounter(workers) {
|
|
16
|
+
const sab = new SharedArrayBuffer((4 + Math.max(workers, 0)) * 4);
|
|
17
|
+
return { sab, view: new Int32Array(sab) };
|
|
18
|
+
}
|
|
19
|
+
export function workerHead(c, workerIndex) {
|
|
20
|
+
return Atomics.load(c.view, 4 + workerIndex);
|
|
21
|
+
}
|
|
22
|
+
/** Worker-side completion: the last decrementer wakes the waiter. */
|
|
23
|
+
export function completeWave(c) {
|
|
24
|
+
if (Atomics.sub(c.view, REMAINING, 1) === 1)
|
|
25
|
+
Atomics.notify(c.view, REMAINING);
|
|
26
|
+
}
|
|
27
|
+
export function setWaveError(c) {
|
|
28
|
+
Atomics.store(c.view, ERROR, 1);
|
|
29
|
+
}
|
|
30
|
+
export function waveErrored(c) {
|
|
31
|
+
return Atomics.load(c.view, ERROR) === 1;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a WaveSync for the chosen tier. `await` MUST loop on Atomics.load(remaining) even after a
|
|
35
|
+
* wake (spurious wakeups + the epoch guard), resolving only when remaining === 0.
|
|
36
|
+
*/
|
|
37
|
+
export function makeWaveSync(tier) {
|
|
38
|
+
function begin(c, batchCount) {
|
|
39
|
+
Atomics.store(c.view, REMAINING, batchCount);
|
|
40
|
+
Atomics.add(c.view, EPOCH, 1); // epoch bump: a stale notify from a previous round is ignored
|
|
41
|
+
Atomics.store(c.view, ERROR, 0);
|
|
42
|
+
}
|
|
43
|
+
function awaitTier1(c) {
|
|
44
|
+
// Tier 1: Atomics.waitAsync — browser main thread (non-blocking). Loop until remaining === 0.
|
|
45
|
+
const step = async () => {
|
|
46
|
+
while (Atomics.load(c.view, REMAINING) !== 0) {
|
|
47
|
+
const r = waitAsync(c.view, REMAINING, Atomics.load(c.view, REMAINING));
|
|
48
|
+
if (r.async)
|
|
49
|
+
await r.value;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return step();
|
|
53
|
+
}
|
|
54
|
+
function awaitTier2(c) {
|
|
55
|
+
// Tier 2: blocking Atomics.wait — Node main thread or coordinator/worker may block directly.
|
|
56
|
+
while (true) {
|
|
57
|
+
const remaining = Atomics.load(c.view, REMAINING);
|
|
58
|
+
if (remaining === 0)
|
|
59
|
+
return;
|
|
60
|
+
Atomics.wait(c.view, REMAINING, remaining);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function awaitTier3(c) {
|
|
64
|
+
// Tier 3: promise-poll — SAB present, waitAsync absent. Poll on a microtask/timeout.
|
|
65
|
+
while (Atomics.load(c.view, REMAINING) !== 0) {
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
begin,
|
|
71
|
+
complete: completeWave,
|
|
72
|
+
await(c) {
|
|
73
|
+
switch (tier) {
|
|
74
|
+
case 'waitAsync':
|
|
75
|
+
return awaitTier1(c);
|
|
76
|
+
case 'coordinator-block':
|
|
77
|
+
return awaitTier2(c);
|
|
78
|
+
case 'promise-poll':
|
|
79
|
+
return awaitTier3(c);
|
|
80
|
+
case 'postMessage':
|
|
81
|
+
// No Atomics fence in the postMessage transport — the pool resolves the round on message
|
|
82
|
+
// completion (pool.ts), so this is never reached. Provide a poll for safety.
|
|
83
|
+
return awaitTier3(c);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=wave-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wave-sync.js","sourceRoot":"","sources":["../../src/workers/wave-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../../src/workers/worker-entry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// The worker thread body. Bootstraps the zero-copy world view from the SAB
|
|
2
|
+
// manifest in workerData, imports the user's kernel module (the dispatch mechanism: kernels are
|
|
3
|
+
// functions, resolved by importing the same source on the worker side — ), then
|
|
4
|
+
// runs a blocking dispatch loop driven entirely by Atomics (no per-wave postMessage on the hot path):
|
|
5
|
+
//
|
|
6
|
+
// 1. Atomics.wait on the wake word for the next dispatch generation (tier-2 blocking wait, OFF the
|
|
7
|
+
// main thread, so the Node main thread may itself block on the wave fence without deadlock).
|
|
8
|
+
// 2. Read the work descriptor (systemId, dt, matched entity indices) from the shared work SAB.
|
|
9
|
+
// 3. Run the system kernel over the indices — field writes to disjoint shared columns, structural
|
|
10
|
+
// ops deferred to THIS worker's SAB command buffer.
|
|
11
|
+
// 4. Store the buffer head into the shared heads SAB, then Atomics.sub-decrement the wave counter
|
|
12
|
+
// (the last decrementer notifies the main thread).
|
|
13
|
+
//
|
|
14
|
+
// The worker NEVER mutates shared structure mid-wave and NEVER reads the bitmask.
|
|
15
|
+
import { parentPort, workerData } from 'node:worker_threads';
|
|
16
|
+
import { makeEncoder, buildFieldCodec } from '../commands/index.js';
|
|
17
|
+
import { buildWorkerWorldView, makeWriteCorralWriter } from './world-view.js';
|
|
18
|
+
import { completeWave, setWaveError } from './wave-sync.js';
|
|
19
|
+
import { takeReserved } from './reservation.js';
|
|
20
|
+
import { NO_ENTITY } from '@ecsia/core';
|
|
21
|
+
const NO_ENTITY_BITS = NO_ENTITY >>> 0;
|
|
22
|
+
const WAKE = 0;
|
|
23
|
+
/** Work descriptor SAB layout: [0]=systemId [1]=count [2]=dtBits(f32) [3..]=entity indices (bytes 12). */
|
|
24
|
+
const WORK_INDICES_BYTE_OFFSET = 12;
|
|
25
|
+
async function main() {
|
|
26
|
+
const boot = workerData;
|
|
27
|
+
const mod = (await import(boot.kernelModule));
|
|
28
|
+
const { kernels, components: defByName } = mod.buildWorkerKernels();
|
|
29
|
+
// The SystemId→name mapping is the POOL's registration order (the bootstrap), NOT the kernel
|
|
30
|
+
// module's own order — so the worker runs the system the pool dispatched, by name.
|
|
31
|
+
const names = boot.systemNames;
|
|
32
|
+
// Align each worker-side ComponentDef id to the main thread's dense assignment
|
|
33
|
+
// so column keys and command-buffer componentIds match byte-for-byte. The kernel
|
|
34
|
+
// module's defineComponent calls leave id UNREGISTERED; the manifest carries the authoritative ids.
|
|
35
|
+
for (const c of boot.components) {
|
|
36
|
+
const def = defByName.get(c.name);
|
|
37
|
+
if (def !== undefined)
|
|
38
|
+
def.id = c.id;
|
|
39
|
+
}
|
|
40
|
+
const reservation = {
|
|
41
|
+
sab: boot.reservationSab,
|
|
42
|
+
view: new Int32Array(boot.reservationSab),
|
|
43
|
+
capacity: boot.reservationCapacity,
|
|
44
|
+
};
|
|
45
|
+
const cb = {
|
|
46
|
+
workerIndex: boot.workerIndex,
|
|
47
|
+
words: new Uint32Array(boot.commandSab),
|
|
48
|
+
head: 0,
|
|
49
|
+
recordCount: 0,
|
|
50
|
+
reservation: { handles: [] },
|
|
51
|
+
reservationCursor: 0,
|
|
52
|
+
appliedCreateCount: 0,
|
|
53
|
+
// The worker's buffer is the SAB the main thread reads in place — it is FIXED, never grown off the
|
|
54
|
+
// shared backing (review issue #3). On overflow `ensureWords` caps and sets `overflowed`.
|
|
55
|
+
fixed: true,
|
|
56
|
+
overflowed: false,
|
|
57
|
+
};
|
|
58
|
+
const codecCache = new Map();
|
|
59
|
+
const codecOf = (def) => {
|
|
60
|
+
const id = def.id;
|
|
61
|
+
let c = codecCache.get(id);
|
|
62
|
+
if (c === undefined) {
|
|
63
|
+
c = buildFieldCodec(def);
|
|
64
|
+
codecCache.set(id, c);
|
|
65
|
+
}
|
|
66
|
+
return c;
|
|
67
|
+
};
|
|
68
|
+
const encoder = makeEncoder({
|
|
69
|
+
cb,
|
|
70
|
+
infoOf(def) {
|
|
71
|
+
const id = def.id;
|
|
72
|
+
return { id, codec: codecOf(def) };
|
|
73
|
+
},
|
|
74
|
+
relationCodec() {
|
|
75
|
+
// Relation payload schemas are not yet replicated into the worker
|
|
76
|
+
// boot manifest, so no payload codec is available here → setRelation emits payloadWordCount=0. The
|
|
77
|
+
// pair add itself still flows (subject, relationId, target); only the payload leg is deferred.
|
|
78
|
+
return undefined;
|
|
79
|
+
},
|
|
80
|
+
warn(message) {
|
|
81
|
+
parentPort?.postMessage({ kind: 'diagnostic', message });
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
// OP_CREATE on the worker takes its handle from the SAB reservation cursor (Atomics.sub take path).
|
|
85
|
+
// On exhaustion takeReserved returns NO_ENTITY (0xffffffff): per
|
|
86
|
+
// NOTHING and return NO_ENTITY — NOT route a NO_ENTITY handle through baseCreate (which would append a
|
|
87
|
+
// spurious OP_CREATE 0xffffffff that the apply path would try to spawn → record-table corruption).
|
|
88
|
+
const baseCreate = encoder.create;
|
|
89
|
+
encoder.create = () => {
|
|
90
|
+
const h = takeReserved(reservation);
|
|
91
|
+
if (h >>> 0 === NO_ENTITY_BITS) {
|
|
92
|
+
parentPort?.postMessage({ kind: 'diagnostic', message: 'command-buffer: reservation exhausted; raise maxSpawnsPerWave (spawn capped)' });
|
|
93
|
+
return h;
|
|
94
|
+
}
|
|
95
|
+
cb.reservation = { handles: [h] };
|
|
96
|
+
cb.reservationCursor = 0;
|
|
97
|
+
return baseCreate();
|
|
98
|
+
};
|
|
99
|
+
const writeCorral = makeWriteCorralWriter(boot.writeCorralSab);
|
|
100
|
+
const view = buildWorkerWorldView(boot.buffers, boot.indexBitsMask, encoder, writeCorral);
|
|
101
|
+
const wake = new Int32Array(boot.wakeSab);
|
|
102
|
+
const work = new Int32Array(boot.workSab);
|
|
103
|
+
const workF32 = new Float32Array(boot.workSab);
|
|
104
|
+
const heads = new Int32Array(boot.waveSab); // word 4+: per-worker head (after the 4 counter words)
|
|
105
|
+
const waveCounter = { sab: boot.waveSab, view: new Int32Array(boot.waveSab) };
|
|
106
|
+
const notice = new Int32Array(boot.noticeSab); // [0] = main thread's published re-backing generation
|
|
107
|
+
const HEAD_WORD = 4 + boot.workerIndex;
|
|
108
|
+
// Pending `columns-added` broadcasts. The dispatch loop blocks on Atomics, so
|
|
109
|
+
// a posted message only drains when the loop yields to the event loop; on a NOTICE round the loop
|
|
110
|
+
// `await`s here so the queued broadcast is delivered before we re-wrap.
|
|
111
|
+
const noticeQueue = [];
|
|
112
|
+
let noticeWaiter;
|
|
113
|
+
let appliedNoticeGen = 0;
|
|
114
|
+
parentPort?.on('message', (msg) => {
|
|
115
|
+
if (msg?.kind === 'columns-added') {
|
|
116
|
+
noticeQueue.push(msg);
|
|
117
|
+
noticeWaiter?.();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const nextNotice = async () => {
|
|
121
|
+
if (noticeQueue.length > 0)
|
|
122
|
+
return noticeQueue.shift();
|
|
123
|
+
await new Promise((resolve) => {
|
|
124
|
+
noticeWaiter = resolve;
|
|
125
|
+
});
|
|
126
|
+
noticeWaiter = undefined;
|
|
127
|
+
return noticeQueue.shift();
|
|
128
|
+
};
|
|
129
|
+
let lastWake = 0;
|
|
130
|
+
parentPort?.postMessage({ kind: 'ready', workerIndex: boot.workerIndex });
|
|
131
|
+
while (true) {
|
|
132
|
+
Atomics.wait(wake, WAKE, lastWake);
|
|
133
|
+
const signal = Atomics.load(wake, WAKE);
|
|
134
|
+
if (signal === lastWake)
|
|
135
|
+
continue;
|
|
136
|
+
lastWake = signal;
|
|
137
|
+
if (signal < 0)
|
|
138
|
+
return; // shutdown sentinel
|
|
139
|
+
// NOTICE round: a column re-backed on the main thread. Drain the queued
|
|
140
|
+
// `columns-added` broadcast(s) up to the published generation, re-wrap the new SABs, then ACK by
|
|
141
|
+
// completing the wave fence. No system runs this wave; the next dispatch proceeds only after this.
|
|
142
|
+
const publishedGen = Atomics.load(notice, 0);
|
|
143
|
+
if (publishedGen !== appliedNoticeGen) {
|
|
144
|
+
try {
|
|
145
|
+
while (appliedNoticeGen !== publishedGen) {
|
|
146
|
+
const msg = await nextNotice();
|
|
147
|
+
view.applyColumnGrowth(msg.columns.map((c) => ({ key: c.key, backing: c.backing, layout: c.layout })));
|
|
148
|
+
appliedNoticeGen = msg.generation;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
setWaveError(waveCounter);
|
|
153
|
+
parentPort?.postMessage({ kind: 'error', message: String(err) });
|
|
154
|
+
}
|
|
155
|
+
completeWave(waveCounter);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const systemId = Atomics.load(work, 0);
|
|
159
|
+
const count = Atomics.load(work, 1);
|
|
160
|
+
const dt = workF32[2];
|
|
161
|
+
cb.head = 0;
|
|
162
|
+
cb.recordCount = 0;
|
|
163
|
+
cb.overflowed = false;
|
|
164
|
+
writeCorral.reset();
|
|
165
|
+
const name = names[systemId];
|
|
166
|
+
const kernel = name !== undefined ? kernels.get(name) : undefined;
|
|
167
|
+
try {
|
|
168
|
+
if (kernel !== undefined && count > 0) {
|
|
169
|
+
const indices = new Int32Array(boot.workSab, WORK_INDICES_BYTE_OFFSET, count);
|
|
170
|
+
kernel(view, indices, dt);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
setWaveError(waveCounter);
|
|
175
|
+
parentPort?.postMessage({ kind: 'error', message: String(err) });
|
|
176
|
+
}
|
|
177
|
+
if (cb.overflowed) {
|
|
178
|
+
// The fixed SAB buffer filled: records were capped (not lost off-SAB, not a crash). `head` still
|
|
179
|
+
// lands on a record boundary <= the SAB length, so the main-thread apply reads exactly what fit.
|
|
180
|
+
parentPort?.postMessage({ kind: 'diagnostic', message: `command-buffer overflow on worker ${boot.workerIndex}; raise commandWords (records capped this wave)` });
|
|
181
|
+
}
|
|
182
|
+
Atomics.store(heads, HEAD_WORD, cb.head);
|
|
183
|
+
completeWave(waveCounter);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
void main();
|
|
187
|
+
//# sourceMappingURL=worker-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../../src/workers/worker-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ComponentDef, Schema } from '@ecsia/schema';
|
|
2
|
+
import type { WorkerWorldView } from './world-view.js';
|
|
3
|
+
import type { OrderingHint, SystemDef } from '../planner/index.js';
|
|
4
|
+
export interface WorkerSystemKernel {
|
|
5
|
+
(view: WorkerWorldView, indices: Int32Array, dt: number): void;
|
|
6
|
+
}
|
|
7
|
+
export interface WorkerSystemDef {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly read?: readonly ComponentDef<Schema>[];
|
|
10
|
+
readonly write?: readonly ComponentDef<Schema>[];
|
|
11
|
+
readonly before?: readonly WorkerSystemDef[];
|
|
12
|
+
readonly after?: readonly WorkerSystemDef[];
|
|
13
|
+
readonly order?: readonly OrderingHint[];
|
|
14
|
+
readonly maxSpawnsPerWave?: number;
|
|
15
|
+
/** The worker-runnable per-batch body. Receives the matched entity indices for this dispatch. */
|
|
16
|
+
readonly kernel: WorkerSystemKernel;
|
|
17
|
+
}
|
|
18
|
+
/** Components a worker system reads + writes — the matching set the main thread uses for dispatch. */
|
|
19
|
+
export declare function matchComponentsOf(def: WorkerSystemDef): readonly ComponentDef<Schema>[];
|
|
20
|
+
/**
|
|
21
|
+
* A worker system carries its `kernel` on the SystemDef so the executor can dispatch it to a worker
|
|
22
|
+
* AND run it on the main thread. The `run` body (used by the single-thread executor's runSystem path)
|
|
23
|
+
* is a thin adapter the scheduler installs when it has the main-thread WorkerWorldView; until then it
|
|
24
|
+
* throws if invoked outside the worker-aware executor.
|
|
25
|
+
*/
|
|
26
|
+
export interface WorkerSystemBox {
|
|
27
|
+
readonly def: SystemDef;
|
|
28
|
+
readonly kernel: WorkerSystemKernel;
|
|
29
|
+
readonly matchComponents: readonly ComponentDef<Schema>[];
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=worker-system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-system.d.ts","sourceRoot":"","sources":["../../src/workers/worker-system.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAElE,MAAM,WAAW,kBAAkB;IACjC,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/D;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,YAAY,CAAC,MAAM,CAAC,EAAE,CAAA;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,YAAY,CAAC,MAAM,CAAC,EAAE,CAAA;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;CACpC;AAED,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,SAAS,YAAY,CAAC,MAAM,CAAC,EAAE,CAKvF;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;IACnC,QAAQ,CAAC,eAAe,EAAE,SAAS,YAAY,CAAC,MAAM,CAAC,EAAE,CAAA;CAC1D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// A WORKER-ELIGIBLE system: its body is expressed against the
|
|
2
|
+
// `WorkerWorldView` (shared-SAB column field access + the command encoder) and an explicit list of
|
|
3
|
+
// matched entity indices, so the SAME kernel runs identically on a worker thread (over the shared
|
|
4
|
+
// buffer set) and on the main thread (single-thread mode) — the structural guarantee behind
|
|
5
|
+
// serial-equivalence: there is ONE body, not two.
|
|
6
|
+
//
|
|
7
|
+
// The main thread does the bitmask-free archetype MATCHING (it owns the query engine) and hands the
|
|
8
|
+
// worker the matched entity indices; the worker runs the per-entity kernel reading ARCHETYPE TABLES
|
|
9
|
+
// ONLY (never the bitmask). Field writes go to disjoint shared columns; structural ops
|
|
10
|
+
// are deferred to the worker's command buffer.
|
|
11
|
+
/** Components a worker system reads + writes — the matching set the main thread uses for dispatch. */
|
|
12
|
+
export function matchComponentsOf(def) {
|
|
13
|
+
const set = new Map();
|
|
14
|
+
for (const c of def.read ?? [])
|
|
15
|
+
set.set(c, true);
|
|
16
|
+
for (const c of def.write ?? [])
|
|
17
|
+
set.set(c, true);
|
|
18
|
+
return [...set.keys()];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=worker-system.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-system.js","sourceRoot":"","sources":["../../src/workers/worker-system.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,mGAAmG;AACnG,kGAAkG;AAClG,4FAA4F;AAC5F,kDAAkD;AAClD,EAAE;AACF,oGAAoG;AACpG,oGAAoG;AACpG,uFAAuF;AACvF,+CAA+C;AAsB/C,sGAAsG;AACtG,MAAM,UAAU,iBAAiB,CAAC,GAAoB;IACpD,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAA;IACjD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IAChD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;IACjD,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ColumnGrowthNotice, RegionKey, SharedHandleManifest } from '@ecsia/core';
|
|
2
|
+
import type { CommandEncoder } from '../commands/index.js';
|
|
3
|
+
declare const REC_ARCH_ID: RegionKey;
|
|
4
|
+
declare const REC_ARCH_ROW: RegionKey;
|
|
5
|
+
export interface WorkerWorldView {
|
|
6
|
+
/** (archetypeId, row) of an entity index, from the shared record regions. */
|
|
7
|
+
locationOf(index: number): {
|
|
8
|
+
archetypeId: number;
|
|
9
|
+
row: number;
|
|
10
|
+
};
|
|
11
|
+
/** Read field `fieldIndex` of component `componentId` for entity index `index`. */
|
|
12
|
+
readField(index: number, componentId: number, fieldIndex: number): number;
|
|
13
|
+
/** Write field `fieldIndex` of component `componentId` for entity index `index` (disjoint SAB write). */
|
|
14
|
+
writeField(index: number, componentId: number, fieldIndex: number, value: number): void;
|
|
15
|
+
/** The structural-op encoder for this worker (defers create/destroy/add/remove to the command buffer). */
|
|
16
|
+
readonly commands: CommandEncoder;
|
|
17
|
+
/**
|
|
18
|
+
* Re-wrap re-backed columns onto their new SAB. The
|
|
19
|
+
* pool drains the main thread's re-backing journal at the wave fence and delivers the new backings
|
|
20
|
+
* here BEFORE the next dispatch, so the worker stops reading the abandoned buffer. In-place `.grow()`
|
|
21
|
+
* never produces a notice — those views length-track automatically.
|
|
22
|
+
*/
|
|
23
|
+
applyColumnGrowth(notices: readonly ColumnGrowthNotice[]): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Worker write-corral writer. Wraps the per-worker corral SAB. `push`
|
|
27
|
+
* stages one `(index, componentId)` value-write entry: word [0] is the running entry count, entries
|
|
28
|
+
* follow as `[index, componentId]` pairs. Single-writer (this worker only), no atomics — the main
|
|
29
|
+
* thread reads it only after the wave fence. On overflow the corral caps (the merge would read past
|
|
30
|
+
* the SAB otherwise); the cap is diagnosed at merge time by the pool.
|
|
31
|
+
*/
|
|
32
|
+
export interface WriteCorralWriter {
|
|
33
|
+
push(index: number, componentId: number): void;
|
|
34
|
+
reset(): void;
|
|
35
|
+
}
|
|
36
|
+
export declare function makeWriteCorralWriter(sab: SharedArrayBuffer): WriteCorralWriter;
|
|
37
|
+
export declare function buildWorkerWorldView(manifest: SharedHandleManifest, indexBitsMask: number, commands: CommandEncoder, writeCorral?: WriteCorralWriter): WorkerWorldView;
|
|
38
|
+
export { REC_ARCH_ID, REC_ARCH_ROW };
|
|
39
|
+
//# sourceMappingURL=world-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-view.d.ts","sourceRoot":"","sources":["../../src/workers/world-view.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAA0B,SAAS,EAAE,oBAAoB,EAAc,MAAM,aAAa,CAAA;AAC1H,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,QAAA,MAAM,WAAW,EAA2B,SAAS,CAAA;AACrD,QAAA,MAAM,YAAY,EAA4B,SAAS,CAAA;AAavD,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/D,mFAAmF;IACnF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAA;IACzE,yGAAyG;IACzG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACvF,0GAA0G;IAC1G,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC;;;;;OAKG;IACH,iBAAiB,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAAA;CAChE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9C,KAAK,IAAI,IAAI,CAAA;CACd;AAKD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,iBAAiB,GAAG,iBAAiB,CAgB/E;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,oBAAoB,EAC9B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,eAAe,CAiDjB;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// The worker-side (and main-thread-mirror) zero-copy view over the shared buffer set. It
|
|
2
|
+
// re-wraps every column + region SAB by reference (LENGTH-TRACKING views) so a worker
|
|
3
|
+
// reads/writes Position.x as a direct indexed load on the SAME
|
|
4
|
+
// Float32Array the main thread uses — no value copy. Workers read ARCHETYPE TABLES ONLY here: an
|
|
5
|
+
// entity's (archetypeId, row) comes from the shared entity-record regions, never the bitmask
|
|
6
|
+
import { elementCtor } from '@ecsia/core';
|
|
7
|
+
const REC_ARCH_ID = 'entity.archetypeId';
|
|
8
|
+
const REC_ARCH_ROW = 'entity.archetypeRow';
|
|
9
|
+
/** Re-wrap a manifest column/region SAB as a length-tracking view of its element kind. */
|
|
10
|
+
function wrap(backing, element) {
|
|
11
|
+
const Ctor = elementCtor(element);
|
|
12
|
+
return new Ctor(backing);
|
|
13
|
+
}
|
|
14
|
+
const CORRAL_COUNT_WORD = 0;
|
|
15
|
+
const CORRAL_HEADER_WORDS = 1;
|
|
16
|
+
export function makeWriteCorralWriter(sab) {
|
|
17
|
+
const view = new Uint32Array(sab);
|
|
18
|
+
const capacityPairs = (view.length - CORRAL_HEADER_WORDS) >>> 1;
|
|
19
|
+
return {
|
|
20
|
+
push(index, componentId) {
|
|
21
|
+
const n = view[CORRAL_COUNT_WORD];
|
|
22
|
+
if (n >= capacityPairs)
|
|
23
|
+
return; // capped; pool diagnoses on merge
|
|
24
|
+
const base = CORRAL_HEADER_WORDS + n * 2;
|
|
25
|
+
view[base] = index >>> 0;
|
|
26
|
+
view[base + 1] = componentId >>> 0;
|
|
27
|
+
view[CORRAL_COUNT_WORD] = n + 1;
|
|
28
|
+
},
|
|
29
|
+
reset() {
|
|
30
|
+
view[CORRAL_COUNT_WORD] = 0;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function buildWorkerWorldView(manifest, indexBitsMask, commands, writeCorral) {
|
|
35
|
+
// archetypeId:componentId.fieldIndex → column view.
|
|
36
|
+
const columns = new Map();
|
|
37
|
+
for (const col of manifest.columns) {
|
|
38
|
+
columns.set(col.key, { view: wrap(col.backing, col.layout.element), stride: col.layout.stride });
|
|
39
|
+
}
|
|
40
|
+
const regions = new Map();
|
|
41
|
+
for (const reg of manifest.regions) {
|
|
42
|
+
regions.set(reg.key, wrap(reg.backing, reg.element));
|
|
43
|
+
}
|
|
44
|
+
const archIdRegion = regions.get(REC_ARCH_ID);
|
|
45
|
+
const archRowRegion = regions.get(REC_ARCH_ROW);
|
|
46
|
+
if (archIdRegion === undefined || archRowRegion === undefined) {
|
|
47
|
+
throw new Error('worker view: entity-record regions missing from manifest (not SAB-backed?)');
|
|
48
|
+
}
|
|
49
|
+
function colKey(archetypeId, componentId, fieldIndex) {
|
|
50
|
+
return `${archetypeId}:${componentId}.${fieldIndex}`;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
locationOf(index) {
|
|
54
|
+
return { archetypeId: archIdRegion[index] >>> 0, row: archRowRegion[index] >>> 0 };
|
|
55
|
+
},
|
|
56
|
+
readField(index, componentId, fieldIndex) {
|
|
57
|
+
const { archetypeId, row } = this.locationOf(index);
|
|
58
|
+
const col = columns.get(colKey(archetypeId, componentId, fieldIndex));
|
|
59
|
+
if (col === undefined)
|
|
60
|
+
return 0;
|
|
61
|
+
return col.view[row * col.stride];
|
|
62
|
+
},
|
|
63
|
+
writeField(index, componentId, fieldIndex, value) {
|
|
64
|
+
const { archetypeId, row } = this.locationOf(index);
|
|
65
|
+
const col = columns.get(colKey(archetypeId, componentId, fieldIndex));
|
|
66
|
+
if (col === undefined)
|
|
67
|
+
return;
|
|
68
|
+
col.view[row * col.stride] = value;
|
|
69
|
+
// Stage the value write into this worker's corral so the serial merge feeds the write log
|
|
70
|
+
// (drives onChange + `.changed` for worker writes). fieldIndex collapses to component granularity
|
|
71
|
+
// — matching the main-thread trackWrite default.
|
|
72
|
+
writeCorral?.push(index, componentId);
|
|
73
|
+
},
|
|
74
|
+
commands,
|
|
75
|
+
applyColumnGrowth(notices) {
|
|
76
|
+
for (const n of notices) {
|
|
77
|
+
columns.set(n.key, { view: wrap(n.backing, n.layout.element), stride: n.layout.stride });
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
// indexBitsMask reserved for handle→index narrowing in callers; kept for API symmetry.
|
|
82
|
+
void indexBitsMask;
|
|
83
|
+
}
|
|
84
|
+
export { REC_ARCH_ID, REC_ARCH_ROW };
|
|
85
|
+
//# sourceMappingURL=world-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-view.js","sourceRoot":"","sources":["../../src/workers/world-view.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ecsia/scheduler",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "System scheduler for ecsia: declare what each system reads and writes, and non-conflicting systems run in parallel across worker threads.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ecs",
|
|
7
|
+
"entity-component-system",
|
|
8
|
+
"ecsia",
|
|
9
|
+
"scheduler",
|
|
10
|
+
"parallel",
|
|
11
|
+
"worker-threads",
|
|
12
|
+
"atomics",
|
|
13
|
+
"data-oriented",
|
|
14
|
+
"typescript"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Andy Aragon",
|
|
18
|
+
"homepage": "https://github.com/andymai/ecsia",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/andymai/ecsia.git",
|
|
22
|
+
"directory": "packages/scheduler"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/andymai/ecsia/issues"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.13"
|
|
29
|
+
},
|
|
30
|
+
"type": "module",
|
|
31
|
+
"sideEffects": false,
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"main": "./dist/index.js",
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@ecsia/core": "0.1.0",
|
|
48
|
+
"@ecsia/schema": "0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc -b"
|
|
52
|
+
}
|
|
53
|
+
}
|