40 lines
1.9 KiB
JavaScript
40 lines
1.9 KiB
JavaScript
// Browser transport only; the simulation owns the schedule and shared state.
|
|
let threadStarting = null;
|
|
async function startThreads() {
|
|
const count = Math.max(1, Math.min(16, Math.floor(SET.workerCap) || 16, (navigator.hardwareConcurrency || 3) - 2));
|
|
if (simPool && simPool.world === mat && simPool.workers.length === count) return;
|
|
if (threadStarting) await threadStarting;
|
|
if (simPool && simPool.world === mat && simPool.workers.length === count) return;
|
|
threadStarting = (async () => {
|
|
if (simPool) for (const worker of simPool.workers) worker.terminate();
|
|
simPool = null;
|
|
if (!(mat.buffer instanceof SharedArrayBuffer)) throw Error('Shared memory is unavailable');
|
|
const url = URL.createObjectURL(new Blob([SIM_WORKER_SOURCE, '\nonmessage=e=>threadMessage(e.data);'], {type:'text/javascript'}));
|
|
const pool = { world: mat, workers: [], slots: [] };
|
|
try {
|
|
const ready = [];
|
|
for (let k = 0; k < count; k++) {
|
|
const worker = new Worker(url), slot = threadSlot();
|
|
pool.workers.push(worker); pool.slots.push(slot);
|
|
ready.push(new Promise((resolve,reject) => {
|
|
const deadline = performance.now() + 10000;
|
|
worker.onerror = e => {
|
|
const error = Error(e.message || 'Simulation worker failed');
|
|
threadFailure(slot, error); reject(error);
|
|
};
|
|
const poll = () => {
|
|
if (Atomics.load(slot.control,0)) resolve();
|
|
else if (performance.now() > deadline) reject(Error('Simulation worker startup timed out'));
|
|
else setTimeout(poll, 1);
|
|
};
|
|
poll();
|
|
}));
|
|
worker.postMessage({world:threadWorld(),slot});
|
|
}
|
|
await Promise.all(ready); simPool = pool;
|
|
} catch (e) { for (const worker of pool.workers) worker.terminate(); throw e; }
|
|
finally { URL.revokeObjectURL(url); }
|
|
})();
|
|
try { await threadStarting; } finally { threadStarting = null; }
|
|
}
|