|
'use strict'; |
|
require('@ungap/with-resolvers'); |
|
const {MAIN, THREAD} = require('./channel.js'); |
|
const {APPLY} = require('./shared/traps.js'); |
|
const {assign} = require('./shared/utils.js'); |
|
const $coincident = (require('./structured.js')); |
|
const JSON = (require('./json.js')); |
|
const main = (require('./window/main.js')); |
|
const thread = (require('./window/thread.js')); |
|
const serverMain = (require('./server/main.js')); |
|
const serverThread = (require('./server/thread.js')); |
|
const Worker = (require('./shared/worker.js')); |
|
|
|
const {notify, wait} = Atomics; |
|
const {parse, stringify} = JSON; |
|
|
|
const isServer = !!globalThis.process; |
|
const proxies = new WeakMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const parseData = data => { |
|
let id; |
|
if (/^!(-?\d+)?/.test(data)) { |
|
id = RegExp.$1; |
|
data = data.slice(1 + id.length); |
|
} |
|
return {id, result: data ? parse(data) : void 0}; |
|
}; |
|
|
|
const coincident = isServer ? |
|
|
|
(wss, globals) => wss.on('connection', ws => { |
|
|
|
ws.once('message', buffer => { |
|
let id = 0; |
|
const [SERVER_MAIN, SERVER_THREAD] = parse(buffer); |
|
const resolvers = new Map; |
|
|
|
const {[SERVER_MAIN]: __main__} = serverMain( |
|
{[SERVER_THREAD]: async (trap, ...args) => { |
|
const data = stringify([trap, ...args]); |
|
|
|
if (trap === APPLY) { |
|
const {promise, resolve} = Promise.withResolvers(); |
|
const uid = String(id++); |
|
resolvers.set(uid, resolve); |
|
ws.send('!' + uid + data); |
|
return await promise; |
|
} |
|
ws.send('!' + data); |
|
}}, |
|
SERVER_MAIN, |
|
SERVER_THREAD, |
|
globals |
|
).proxy; |
|
|
|
ws |
|
|
|
.on('close', () => { |
|
for (const [_, resolve] of resolvers) resolve(); |
|
}) |
|
|
|
.on('message', buffer => { |
|
const {id, result} = parseData(String(buffer)); |
|
if (id) { |
|
const resolve = resolvers.get(id); |
|
resolvers.delete(id); |
|
resolve(result); |
|
} |
|
else |
|
ws.send(stringify(__main__(...result))); |
|
}) |
|
|
|
.send(''); |
|
}); |
|
}) : |
|
|
|
|
|
(self, ws, ...args) => { |
|
const proxy = $coincident(self, ...args); |
|
if (!proxies.has(proxy)) { |
|
const util = self instanceof Worker ? mainBridge : threadBridge; |
|
proxies.set(proxy, util(self, proxy, MAIN, THREAD, ws)); |
|
} |
|
return proxies.get(proxy); |
|
} |
|
; |
|
|
|
if (!isServer) |
|
coincident.transfer = $coincident.transfer; |
|
|
|
module.exports = coincident; |
|
|
|
const mainBridge = (self, thread, MAIN, THREAD, ws) => { |
|
|
|
self.addEventListener('message', ({data: [CHANNEL, i32]}) => { |
|
const SERVER_MAIN = 'M' + CHANNEL; |
|
const SERVER_THREAD = 'T' + CHANNEL; |
|
const {[SERVER_THREAD]: __thread__} = thread; |
|
let resolve; |
|
thread[SERVER_MAIN] = (...args) => new Promise($ => { |
|
resolve = $; |
|
ws.send(stringify(args)); |
|
}); |
|
|
|
ws.addEventListener('message', () => { |
|
|
|
ws.addEventListener('message', async ({data}) => { |
|
const {id, result} = parseData(data); |
|
if (id != null) { |
|
const invoke = __thread__(...result); |
|
if (id) { |
|
const out = await invoke; |
|
ws.send('!' + id + (out === void 0 ? '' : stringify(out))); |
|
} |
|
} |
|
else if (resolve) resolve = resolve(result); |
|
}); |
|
|
|
i32[0] = 1; |
|
notify(i32, 0); |
|
}, {once: true}); |
|
ws.send(stringify([SERVER_MAIN, SERVER_THREAD])); |
|
}, {once: true}); |
|
return main(thread, MAIN, THREAD); |
|
}; |
|
|
|
const threadBridge = (self, proxy, MAIN, THREAD) => { |
|
|
|
|
|
const CHANNEL = 'S' + crypto.randomUUID(); |
|
const i32 = new Int32Array(new SharedArrayBuffer(4)); |
|
self.postMessage([CHANNEL, i32]); |
|
wait(i32, 0); |
|
|
|
return assign( |
|
serverThread(proxy, 'M' + CHANNEL, 'T' + CHANNEL), |
|
thread(proxy, MAIN, THREAD) |
|
); |
|
}; |
|
|