Comparing version 1.2.3 to 2.0.0
@@ -12,2 +12,3 @@ const EventEmitter = require('events').EventEmitter | ||
const observer = require('observable-webworkers') | ||
const globalThis = require('globalthis')() | ||
@@ -59,7 +60,7 @@ const handleWorkerLockRequest = (emitter, masterEvent, requestType, releaseType, grantType) => { | ||
const makeWorkerLockRequest = (global, name, requestType, grantType, releaseType) => { | ||
return (fn) => { | ||
const makeWorkerLockRequest = (name, requestType, grantType, releaseType) => { | ||
return () => { | ||
const id = shortid.generate() | ||
global.postMessage({ | ||
globalThis.postMessage({ | ||
type: requestType, | ||
@@ -70,3 +71,3 @@ identifier: id, | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
const listener = (event) => { | ||
@@ -83,27 +84,17 @@ if (!event || !event.data) { | ||
if (responseEvent && responseEvent.type === grantType && responseEvent.identifier === id) { | ||
global.removeEventListener('message', listener) | ||
globalThis.removeEventListener('message', listener) | ||
let error | ||
fn() | ||
.catch((err) => { | ||
error = err | ||
// grant lock | ||
resolve(() => { | ||
// release lock | ||
globalThis.postMessage({ | ||
type: releaseType, | ||
identifier: id, | ||
name | ||
}) | ||
.then((result) => { | ||
global.postMessage({ | ||
type: releaseType, | ||
identifier: id, | ||
name | ||
}) | ||
if (error) { | ||
return reject(error) | ||
} | ||
return resolve(result) | ||
}) | ||
}) | ||
} | ||
} | ||
global.addEventListener('message', listener) | ||
globalThis.addEventListener('message', listener) | ||
}) | ||
@@ -114,3 +105,2 @@ } | ||
const defaultOptions = { | ||
global: global, | ||
singleProcess: false | ||
@@ -121,3 +111,3 @@ } | ||
options = Object.assign({}, defaultOptions, options) | ||
const isMaster = !!options.global.document || options.singleProcess | ||
const isMaster = !!globalThis.document || options.singleProcess | ||
@@ -135,5 +125,5 @@ if (isMaster) { | ||
isWorker: true, | ||
readLock: (name, options) => makeWorkerLockRequest(options.global, name, WORKER_REQUEST_READ_LOCK, MASTER_GRANT_READ_LOCK, WORKER_RELEASE_READ_LOCK), | ||
writeLock: (name, options) => makeWorkerLockRequest(options.global, name, WORKER_REQUEST_WRITE_LOCK, MASTER_GRANT_WRITE_LOCK, WORKER_RELEASE_WRITE_LOCK) | ||
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) | ||
} | ||
} |
@@ -10,2 +10,20 @@ const node = require('./node') | ||
function createReleaseable (queue, options) { | ||
let res | ||
const p = new Promise((resolve) => { | ||
res = resolve | ||
}) | ||
queue.add(() => timeout((() => { | ||
return new Promise((resolve) => { | ||
res(() => { | ||
resolve() | ||
}) | ||
}) | ||
})(), options.timeout)) | ||
return p | ||
} | ||
const createMutex = (name, options) => { | ||
@@ -23,6 +41,6 @@ if (implementation.isWorker) { | ||
return { | ||
readLock: (fn) => { | ||
readLock: () => { | ||
// If there's already a read queue, just add the task to it | ||
if (readQueue) { | ||
return readQueue.add(() => timeout(fn(), options.timeout)) | ||
return createReleaseable(readQueue, options) | ||
} | ||
@@ -38,3 +56,3 @@ | ||
// Add the task to the read queue | ||
const readPromise = readQueue.add(() => timeout(fn(), options.timeout)) | ||
const readPromise = createReleaseable(readQueue, options) | ||
@@ -59,3 +77,3 @@ masterQueue.add(() => { | ||
}, | ||
writeLock: (fn) => { | ||
writeLock: () => { | ||
// Remove the read queue reference, so that any later read locks will be | ||
@@ -66,3 +84,3 @@ // added to a new queue that starts after this write lock has been | ||
return masterQueue.add(() => timeout(fn(), options.timeout)) | ||
return createReleaseable(masterQueue, options) | ||
} | ||
@@ -101,7 +119,17 @@ } | ||
implementation.on('requestReadLock', (name, fn) => { | ||
mutexes[name] && mutexes[name].readLock(fn) | ||
if (!mutexes[name]) { | ||
return | ||
} | ||
mutexes[name].readLock() | ||
.then(release => fn().finally(() => release())) | ||
}) | ||
implementation.on('requestWriteLock', (name, fn) => { | ||
mutexes[name] && mutexes[name].writeLock(fn) | ||
implementation.on('requestWriteLock', async (name, fn) => { | ||
if (!mutexes[name]) { | ||
return | ||
} | ||
mutexes[name].writeLock() | ||
.then(release => fn().finally(() => release())) | ||
}) | ||
@@ -108,0 +136,0 @@ } |
@@ -41,3 +41,3 @@ const EventEmitter = require('events').EventEmitter | ||
const makeWorkerLockRequest = (name, requestType, grantType, releaseType) => { | ||
return (fn) => { | ||
return () => { | ||
const id = shortid.generate() | ||
@@ -51,3 +51,3 @@ | ||
return new Promise((resolve, reject) => { | ||
return new Promise((resolve) => { | ||
const listener = (event) => { | ||
@@ -57,21 +57,11 @@ if (event && event.type === grantType && event.identifier === id) { | ||
let error = null | ||
fn() | ||
.catch((err) => { | ||
error = err | ||
// grant lock | ||
resolve(() => { | ||
// release lock | ||
process.send({ | ||
type: releaseType, | ||
identifier: id, | ||
name | ||
}) | ||
.then((result) => { | ||
process.send({ | ||
type: releaseType, | ||
identifier: id, | ||
name | ||
}) | ||
if (error) { | ||
return reject(error) | ||
} | ||
resolve(result) | ||
}) | ||
}) | ||
} | ||
@@ -107,5 +97,5 @@ } | ||
isWorker: true, | ||
readLock: (name, options) => makeWorkerLockRequest(name, WORKER_REQUEST_READ_LOCK, MASTER_GRANT_READ_LOCK, WORKER_RELEASE_READ_LOCK), | ||
writeLock: (name, options) => makeWorkerLockRequest(name, WORKER_REQUEST_WRITE_LOCK, MASTER_GRANT_WRITE_LOCK, WORKER_RELEASE_WRITE_LOCK) | ||
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) | ||
} | ||
} |
{ | ||
"name": "mortice", | ||
"version": "1.2.3", | ||
"version": "2.0.0", | ||
"description": "Isomorphic read/write lock that works in single processes, node clusters and web workers", | ||
@@ -31,2 +31,3 @@ "main": "lib/index.js", | ||
"browserify": "^16.2.2", | ||
"delay": "^4.3.0", | ||
"execa": "^2.0.0", | ||
@@ -40,6 +41,6 @@ "run-headless": "^2.0.1", | ||
"test/*.test.js" | ||
], | ||
"concurrency": 1 | ||
] | ||
}, | ||
"dependencies": { | ||
"globalthis": "^1.0.0", | ||
"observable-webworkers": "^1.0.0", | ||
@@ -46,0 +47,0 @@ "p-queue": "^6.0.0", |
# mortice | ||
[![Build status](https://travis-ci.org/achingbrain/mortice.svg?branch=master)](https://travis-ci.org/achingbrain/mortice.svg?branch=master) | ||
[![Build status](https://travis-ci.org/achingbrain/mortice.svg?branch=master)](https://travis-ci.org/achingbrain/mortice) | ||
@@ -36,5 +36,2 @@ Isomorphic read/write lock that works in single processes, node clusters and web workers. | ||
// the global object (for use with webworkify and similar) (default: global) | ||
global: window, | ||
// by default the the lock will be held on the main thread, set this to true if the | ||
@@ -45,17 +42,42 @@ // a lock should reside on each worker (default: false) | ||
mutex.readLock(() => { | ||
return Promise.resolve().then(() => console.info('read 1')) | ||
}) | ||
Promise.all([ | ||
(async () => { | ||
const release = await mutex.readLock() | ||
mutex.readLock(() => { | ||
return Promise.resolve().then(() => console.info('read 2')) | ||
}) | ||
try { | ||
console.info('read 1') | ||
} finally { | ||
release() | ||
} | ||
})(), | ||
(async () => { | ||
const release = await mutex.readLock() | ||
mutex.writeLock(() => { | ||
return delay(200).then(() => console.info('write 1')) | ||
}) | ||
try { | ||
console.info('read 2') | ||
} finally { | ||
release() | ||
} | ||
})(), | ||
(async () => { | ||
const release = await mutex.writeLock() | ||
mutex.readLock(() => { | ||
return Promise.resolve().then(() => console.info('read 3')) | ||
}) | ||
try { | ||
await delay(1000) | ||
console.info('write 1') | ||
} finally { | ||
release() | ||
} | ||
})(), | ||
(async () => { | ||
const release = await mutex.readLock() | ||
try { | ||
console.info('read 3') | ||
} finally { | ||
release() | ||
} | ||
})() | ||
]) | ||
``` | ||
@@ -95,9 +117,9 @@ | ||
mutex.readLock(() => { | ||
// return a promise | ||
}) | ||
let release = await mutex.readLock() | ||
// read something | ||
release() | ||
mutex.writeLock(() => { | ||
// return a promise | ||
}) | ||
release = await mutex.writeLock() | ||
// write something | ||
release() | ||
``` | ||
@@ -104,0 +126,0 @@ |
import test from 'ava' | ||
import exec from 'execa' | ||
import path from 'path' | ||
import browserify from 'browserify' | ||
test('executes locks in correct order', (t) => { | ||
return exec('run-headless', { | ||
input: exec('browserify', [path.join(__dirname, 'fixtures', 'browser.js')]).stdout | ||
test('executes locks in correct order', async (t) => { | ||
const result = await exec('run-headless', { | ||
input: browserify([path.join(__dirname, 'fixtures', 'browser.js')]).bundle() | ||
}) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) |
@@ -5,24 +5,46 @@ import test from 'ava' | ||
test('executes locks in correct order', (t) => { | ||
return exec('node', [path.join(__dirname, 'fixtures', 'cluster.js')]) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
test('executes locks in correct order', async (t) => { | ||
const result = await exec('node', [path.join(__dirname, 'fixtures', 'cluster.js')]) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) | ||
test('executes locks in correct order on a single process', (t) => { | ||
return exec('node', [path.join(__dirname, 'fixtures', 'cluster-single-thread.js')]) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
test('executes locks in correct order on a single process', async (t) => { | ||
const result = await exec('node', [path.join(__dirname, 'fixtures', 'cluster-single-thread.js')]) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) |
const mortice = require('../../') | ||
const delay = require('delay') | ||
const mutex = mortice() | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('write 1') | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
console.info(`${type} ${index} waiting`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 1') | ||
const release = await muxex[`${type}Lock`]() | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
console.info(`${type} ${index} start`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 2') | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
resolve() | ||
}) | ||
}) | ||
console.info(`${type} ${index} complete`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
console.info('read 3') | ||
release() | ||
resolve() | ||
}, 500) | ||
}) | ||
}) | ||
if (type === 'read' && index === 4) { | ||
global.__close__() | ||
} | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('write 2') | ||
async function run () { | ||
const mutex = mortice() | ||
resolve() | ||
}) | ||
}) | ||
// queue up read/write requests, the third read should block the second write | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 4') | ||
resolve() | ||
}) | ||
}) | ||
.catch(() => {}) | ||
.then(() => { | ||
global.__close__() | ||
}) | ||
run() |
const cluster = require('cluster') | ||
const mortice = require('../../') | ||
const delay = require('delay') | ||
const mutex = mortice({ | ||
singleProcess: true | ||
}) | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
if (cluster.isMaster) { | ||
cluster.on('message', (worker, message) => { | ||
if (message === 'done') { | ||
worker.kill() | ||
} | ||
}) | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
cluster.fork() | ||
} else { | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('write 1') | ||
console.info(`${type} ${index} waiting`) | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
const release = await muxex[`${type}Lock`]() | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 1') | ||
console.info(`${type} ${index} start`) | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 2') | ||
console.info(`${type} ${index} complete`) | ||
resolve() | ||
}) | ||
}) | ||
release() | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
setTimeout(() => { | ||
console.info('read 3') | ||
if (type === 'read' && index === 4) { | ||
process.send('done') | ||
} | ||
} | ||
resolve() | ||
}, 500) | ||
}) | ||
async function run () { | ||
const mutex = mortice({ | ||
singleProcess: true | ||
}) | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('write 2') | ||
resolve() | ||
if (cluster.isMaster) { | ||
cluster.on('message', (worker, message) => { | ||
if (message === 'done') { | ||
worker.kill() | ||
} | ||
}) | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 4') | ||
cluster.fork() | ||
} else { | ||
// queue up read/write requests, the third read should block the second write | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} | ||
} | ||
resolve() | ||
process.send('done') | ||
}) | ||
}) | ||
} | ||
run() | ||
.then(() => {}) |
const cluster = require('cluster') | ||
const mortice = require('../../') | ||
const delay = require('delay') | ||
const mutex = mortice() | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
if (cluster.isMaster) { | ||
cluster.on('message', (worker, message) => { | ||
if (message === 'done') { | ||
worker.kill() | ||
} | ||
}) | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
cluster.fork() | ||
} else { | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('write 1') | ||
console.info(`${type} ${index} waiting`) | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
const release = await muxex[`${type}Lock`]() | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 1') | ||
console.info(`${type} ${index} start`) | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 2') | ||
console.info(`${type} ${index} complete`) | ||
resolve() | ||
}) | ||
}) | ||
release() | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
setTimeout(() => { | ||
console.info('read 3') | ||
if (type === 'read' && index === 4) { | ||
process.send('done') | ||
} | ||
} | ||
resolve() | ||
}, 500) | ||
}) | ||
}) | ||
async function run () { | ||
const mutex = mortice() | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('write 2') | ||
resolve() | ||
if (cluster.isMaster) { | ||
cluster.on('message', (worker, message) => { | ||
if (message === 'done') { | ||
worker.kill() | ||
} | ||
}) | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
console.info('read 4') | ||
cluster.fork() | ||
} else { | ||
// queue up read/write requests, the third read should block the second write | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} | ||
} | ||
resolve() | ||
process.send('done') | ||
}) | ||
}) | ||
} | ||
run() | ||
.then(() => {}) |
@@ -5,2 +5,3 @@ const work = require('webworkify') | ||
const Worker = mortice.Worker | ||
mortice() | ||
@@ -7,0 +8,0 @@ |
const mortice = require('../../') | ||
const mutex = mortice() | ||
async function read (muxex) { | ||
const release = await muxex.readLock() | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
try { | ||
console.info('read 1') | ||
reject(new Error('err')) | ||
}) | ||
}) | ||
throw new Error('err') | ||
} finally { | ||
release() | ||
} | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
async function write (muxex) { | ||
const release = await muxex.writeLock() | ||
await new Promise((resolve) => { | ||
console.info('write 1') | ||
@@ -19,2 +23,15 @@ | ||
}) | ||
}) | ||
release() | ||
} | ||
async function run () { | ||
const mutex = mortice() | ||
read(mutex) | ||
.catch(() => {}) | ||
write(mutex) | ||
} | ||
run() | ||
.then(() => {}) |
const mortice = require('../../') | ||
const delay = require('delay') | ||
const mutex = mortice() | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 1') | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
setTimeout(() => { | ||
console.info('read 1 complete') | ||
resolve() | ||
}, 500) | ||
}) | ||
}) | ||
console.info(`${type} ${index} waiting`) | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('write 1') | ||
const release = await muxex[`${type}Lock`]() | ||
resolve() | ||
}) | ||
}) | ||
console.info(`${type} ${index} start`) | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
console.info(`${type} ${index} complete`) | ||
release() | ||
} | ||
async function run () { | ||
const mutex = mortice() | ||
// read should complete before write | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
} | ||
run() | ||
.then(() => {}) |
const mortice = require('../../') | ||
const delay = require('delay') | ||
const mutex = mortice() | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('write 1') | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
console.info(`${type} ${index} waiting`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 1') | ||
const release = await muxex[`${type}Lock`]() | ||
resolve() | ||
}) | ||
}) | ||
.then(() => {}) | ||
console.info(`${type} ${index} start`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 2') | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
resolve() | ||
}) | ||
}) | ||
console.info(`${type} ${index} complete`) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
console.info('read 3') | ||
release() | ||
} | ||
resolve() | ||
}, 500) | ||
}) | ||
}) | ||
async function run () { | ||
const mutex = mortice() | ||
mutex.writeLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('write 2') | ||
// queue up read/write requests, the third read should block the second write | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} | ||
resolve() | ||
}) | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve, reject) => { | ||
console.info('read 4') | ||
resolve() | ||
}) | ||
}) | ||
run() | ||
.then(() => {}) |
@@ -6,3 +6,2 @@ const work = require('webworkify') | ||
const observe = require('observable-webworkers') | ||
const worker = work(require('./worker.js')) | ||
@@ -9,0 +8,0 @@ |
const mortice = require('../../') | ||
const globalThis = require('globalthis')() | ||
const delay = require('delay') | ||
module.exports = (self) => { | ||
const mutex = mortice({ | ||
global: self, | ||
singleProcess: true | ||
}) | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'write 1' | ||
}) | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} waiting` | ||
}) | ||
.then(() => {}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 1' | ||
}) | ||
const release = await muxex[`${type}Lock`]() | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} start` | ||
}) | ||
.then(() => {}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 2' | ||
}) | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} complete` | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
setTimeout(() => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 3' | ||
}) | ||
release() | ||
resolve() | ||
}, 500) | ||
if (type === 'read' && index === 4) { | ||
globalThis.postMessage({ | ||
type: 'done' | ||
}) | ||
}) | ||
} | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'write 2' | ||
}) | ||
resolve() | ||
}) | ||
module.exports = () => { | ||
const mutex = mortice({ | ||
singleProcess: true | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 4' | ||
}) | ||
self.postMessage({ | ||
type: 'done' | ||
}) | ||
resolve() | ||
}) | ||
}) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} |
const mortice = require('../../') | ||
const globalThis = require('globalthis')() | ||
const delay = require('delay') | ||
module.exports = (self) => { | ||
const mutex = mortice({ | ||
global: self | ||
}) | ||
const counts = { | ||
read: 0, | ||
write: 0 | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'write 1' | ||
}) | ||
async function lock (type, muxex, timeout = 0) { | ||
counts[type]++ | ||
const index = counts[type] | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} waiting` | ||
}) | ||
.then(() => {}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 1' | ||
}) | ||
const release = await muxex[`${type}Lock`]() | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} start` | ||
}) | ||
.then(() => {}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 2' | ||
}) | ||
if (timeout) { | ||
await delay(timeout) | ||
} | ||
resolve() | ||
}) | ||
globalThis.postMessage({ | ||
type: 'log', | ||
message: `${type} ${index} complete` | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
setTimeout(() => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 3' | ||
}) | ||
release() | ||
resolve() | ||
}, 500) | ||
if (type === 'read' && index === 4) { | ||
globalThis.postMessage({ | ||
type: 'done' | ||
}) | ||
}) | ||
} | ||
} | ||
mutex.writeLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'write 2' | ||
}) | ||
module.exports = () => { | ||
const mutex = mortice() | ||
resolve() | ||
}) | ||
}) | ||
mutex.readLock(() => { | ||
return new Promise((resolve) => { | ||
self.postMessage({ | ||
type: 'log', | ||
message: 'read 4' | ||
}) | ||
self.postMessage({ | ||
type: 'done' | ||
}) | ||
resolve() | ||
}) | ||
}) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex) | ||
lock('read', mutex, 500) | ||
lock('write', mutex) | ||
lock('read', mutex) | ||
} |
import test from 'ava' | ||
import exec from 'execa' | ||
import path from 'path' | ||
import browserify from 'browserify' | ||
test('execute locks in correct order', (t) => { | ||
return exec('run-headless', { | ||
input: exec('browserify', [path.join(__dirname, 'fixtures', 'mortice-workers.js')]).stdout | ||
test('execute locks in correct order', async (t) => { | ||
const result = await exec('run-headless', { | ||
input: browserify([path.join(__dirname, 'fixtures', 'mortice-workers.js')]).bundle() | ||
}) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) |
@@ -5,29 +5,41 @@ import test from 'ava' | ||
test('executes locks in correct order', (t) => { | ||
return exec('node', [path.join(__dirname, 'fixtures', 'process.js')]) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
test('executes locks in correct order', async (t) => { | ||
const result = await exec('node', [path.join(__dirname, 'fixtures', 'process.js')]) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) | ||
test('executes read then waits to start write', (t) => { | ||
return exec('node', [path.join(__dirname, 'fixtures', 'process-read-then-write.js')]) | ||
.then(result => { | ||
t.is(result.stdout, `read 1 | ||
test('executes read then waits to start write', async (t) => { | ||
const result = await exec('node', [path.join(__dirname, 'fixtures', 'process-read-then-write.js')]) | ||
t.is(result.stdout, `read 1 waiting | ||
write 1 waiting | ||
read 1 start | ||
read 1 complete | ||
write 1`) | ||
}) | ||
write 1 start | ||
write 1 complete`) | ||
}) | ||
test('continues processing after error', (t) => { | ||
return exec('node', [path.join(__dirname, 'fixtures', 'process-error-handling.js')]) | ||
.then(result => { | ||
t.is(result.stdout, `read 1 | ||
test('continues processing after error', async (t) => { | ||
const result = await exec('node', [path.join(__dirname, 'fixtures', 'process-error-handling.js')]) | ||
t.is(result.stdout, `read 1 | ||
write 1`) | ||
}) | ||
}) |
import test from 'ava' | ||
import exec from 'execa' | ||
import path from 'path' | ||
import browserify from 'browserify' | ||
test('execute locks in correct order', (t) => { | ||
return exec('run-headless', { | ||
input: exec('browserify', [path.join(__dirname, 'fixtures', 'web-workers.js')]).stdout | ||
test('execute locks in correct order', async (t) => { | ||
const result = await exec('run-headless', { | ||
input: browserify([path.join(__dirname, 'fixtures', 'web-workers.js')]).bundle() | ||
}) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) | ||
test('execute locks in correct order on single thread', (t) => { | ||
return exec('run-headless', { | ||
input: exec('browserify', [path.join(__dirname, 'fixtures', 'web-workers-single-thread.js')]).stdout | ||
test('execute locks in correct order on single thread', async (t) => { | ||
const result = await exec('run-headless', { | ||
input: browserify([path.join(__dirname, 'fixtures', 'web-workers-single-thread.js')]).bundle() | ||
}) | ||
.then(result => { | ||
t.is(result.stdout, `write 1 | ||
read 1 | ||
read 2 | ||
read 3 | ||
write 2 | ||
read 4`) | ||
}) | ||
t.is(result.stdout, `write 1 waiting | ||
read 1 waiting | ||
read 2 waiting | ||
read 3 waiting | ||
write 2 waiting | ||
read 4 waiting | ||
write 1 start | ||
write 1 complete | ||
read 1 start | ||
read 1 complete | ||
read 2 start | ||
read 2 complete | ||
read 3 start | ||
read 3 complete | ||
write 2 start | ||
write 2 complete | ||
read 4 start | ||
read 4 complete`) | ||
}) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38899
847
135
5
7
3
+ Addedglobalthis@^1.0.0
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedglobalthis@1.0.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedobject-keys@1.1.1(transitive)