UNPKG

mortice

Version:

Isomorphic read/write lock that works in single processes, node clusters and web workers

108 lines (92 loc) 3.46 kB
import cluster from 'cluster' import { WORKER_REQUEST_READ_LOCK, WORKER_RELEASE_READ_LOCK, MASTER_GRANT_READ_LOCK, WORKER_REQUEST_WRITE_LOCK, WORKER_RELEASE_WRITE_LOCK, MASTER_GRANT_WRITE_LOCK } from './constants.js' import { nanoid } from './utils.js' import type { MorticeImplementation, MorticeOptions, Release } from './index.js' import type { Worker } from 'cluster' interface RequestEvent { type: string identifier: string name: string } const handleWorkerLockRequest = (emitter: EventTarget, masterEvent: string, requestType: string, releaseType: string, grantType: string) => { return (worker: Worker, requestEvent: RequestEvent) => { if (requestEvent != null && requestEvent.type === requestType) { emitter.dispatchEvent(new MessageEvent(masterEvent, { data: { name: requestEvent.name, handler: async () => { // grant lock to worker worker.send({ type: grantType, name: requestEvent.name, identifier: requestEvent.identifier }) // wait for worker to finish await new Promise<void>((resolve) => { const releaseEventListener = (releaseEvent: RequestEvent): void => { if (releaseEvent.type === releaseType && releaseEvent.identifier === requestEvent.identifier) { worker.removeListener('message', releaseEventListener) resolve() } } worker.on('message', releaseEventListener) }) } } })) } } } const makeWorkerLockRequest = (name: string, requestType: string, grantType: string, releaseType: string) => { return async () => { const id = nanoid() if (process.send == null) { throw new Error('No send method on process - are we a cluster worker?') } process.send({ type: requestType, identifier: id, name }) return new Promise<Release>((resolve) => { const listener = (event: RequestEvent): void => { if (event.type === grantType && event.identifier === id) { process.removeListener('message', listener) // grant lock resolve(() => { if (process.send == null) { throw new Error('No send method on process - are we a cluster worker?') } // release lock process.send({ type: releaseType, identifier: id, name }) }) } } process.on('message', listener) }) } } export default (options: Required<MorticeOptions>): MorticeImplementation | EventTarget | undefined => { if (cluster.isPrimary || options.singleProcess) { const emitter = new EventTarget() cluster.on('message', handleWorkerLockRequest(emitter, 'requestReadLock', WORKER_REQUEST_READ_LOCK, WORKER_RELEASE_READ_LOCK, MASTER_GRANT_READ_LOCK)) cluster.on('message', handleWorkerLockRequest(emitter, 'requestWriteLock', WORKER_REQUEST_WRITE_LOCK, WORKER_RELEASE_WRITE_LOCK, MASTER_GRANT_WRITE_LOCK)) return emitter } return { isWorker: true, readLock: (name) => makeWorkerLockRequest(name, WORKER_REQUEST_READ_LOCK, MASTER_GRANT_READ_LOCK, WORKER_RELEASE_READ_LOCK), writeLock: (name) => makeWorkerLockRequest(name, WORKER_REQUEST_WRITE_LOCK, MASTER_GRANT_WRITE_LOCK, WORKER_RELEASE_WRITE_LOCK) } }