UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

90 lines 4.09 kB
import { Thread } from "@chainsafe/threads"; import { sleep } from "@lodestar/utils"; import { EventDirection, NetworkEvent } from "../network/events.js"; const NANO_TO_SECOND_CONVERSION = 1e9; /** * Bridges events from worker to main thread * Each event can only have one direction: * - worker to main * - main to worker */ export function wireEventsOnWorkerThread(mainEventName, events, parentPort, metrics, isWorkerToMain) { // Subscribe to events from main thread parentPort.on("message", (data) => { if (typeof data === "object" && data.type === mainEventName && // This check is not necessary but added for safety in case of improper implemented events isWorkerToMain[data.event] === EventDirection.mainToWorker) { const [sec, nanoSec] = process.hrtime(data.posted); const networkWorkerLatency = sec + nanoSec / NANO_TO_SECOND_CONVERSION; metrics?.networkWorkerWireEventsOnWorkerThreadLatency.observe({ eventName: data.event }, networkWorkerLatency); events.emit(data.event, data.data); } }); for (const eventName of Object.keys(isWorkerToMain)) { if (isWorkerToMain[eventName] === EventDirection.workerToMain) { // Pick one of the events to comply with StrictEventEmitter function signature events.on(eventName, (data) => { const workerEvent = { type: mainEventName, event: eventName, posted: process.hrtime(), data, }; let transferList = undefined; if (eventName === NetworkEvent.pendingGossipsubMessage) { const payload = data; // Transfer the underlying ArrayBuffer to avoid copy for PendingGossipsubMessage transferList = [payload.msg.data.buffer]; } parentPort.postMessage(workerEvent, transferList); }); } } } export function wireEventsOnMainThread(mainEventName, events, worker, metrics, isWorkerToMain) { // Subscribe to events from main thread worker.on("message", (data) => { if (typeof data === "object" && data.type === mainEventName && // This check is not necessary but added for safety in case of improper implemented events isWorkerToMain[data.event] === EventDirection.workerToMain) { const [sec, nanoSec] = process.hrtime(data.posted); const networkWorkerLatency = sec + nanoSec / NANO_TO_SECOND_CONVERSION; metrics?.networkWorkerWireEventsOnMainThreadLatency.observe({ eventName: data.event }, networkWorkerLatency); events.emit(data.event, data.data); } }); for (const eventName of Object.keys(isWorkerToMain)) { if (isWorkerToMain[eventName] === EventDirection.mainToWorker) { // Pick one of the events to comply with StrictEventEmitter function signature events.on(eventName, (data) => { const workerEvent = { type: mainEventName, event: eventName, posted: process.hrtime(), data, }; worker.postMessage(workerEvent); }); } } } export async function terminateWorkerThread({ worker, retryMs, retryCount, logger, }) { const terminated = new Promise((resolve) => { Thread.events(worker).subscribe((event) => { if (event.type === "termination") { resolve(true); } }); }); for (let i = 0; i < retryCount; i++) { await Thread.terminate(worker); const result = await Promise.race([terminated, sleep(retryMs).then(() => false)]); if (result) return; logger?.warn("Worker thread failed to terminate, retrying..."); } throw new Error(`Worker thread failed to terminate in ${retryCount * retryMs}ms.`); } //# sourceMappingURL=workerEvents.js.map