Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@matrixai/async-locks

Package Overview
Dependencies
Maintainers
4
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@matrixai/async-locks - npm Package Compare versions

Comparing version 2.3.1 to 3.0.0

4

dist/Lock.js

@@ -27,4 +27,8 @@ "use strict";

}
let released = false;
return [
async () => {
if (released)
return;
released = true;
--this._count;

@@ -31,0 +35,0 @@ release();

19

dist/LockBox.d.ts
import type { ResourceAcquire } from '@matrixai/resources';
import type { Lockable, ToString, LockRequest } from './types';
declare class LockBox<L extends Lockable> implements Lockable {
import type { ToString, Lockable, MultiLockRequest, MultiLockAcquire, MultiLockAcquired } from './types';
declare class LockBox<L extends Lockable = Lockable> implements Lockable {
protected _locks: Map<string, L>;
lock(...requests: Array<LockRequest<L>>): ResourceAcquire<LockBox<L>>;
lock(...requests: Array<MultiLockRequest<L>>): ResourceAcquire<LockBox<L>>;
lockMulti(...requests: Array<MultiLockRequest<L>>): Array<MultiLockAcquire<L>>;
get locks(): ReadonlyMap<string, L>;

@@ -11,10 +12,18 @@ get count(): number;

withF<T>(...params: [
...requests: Array<LockRequest<L>>,
...requests: Array<MultiLockRequest<L>>,
f: (lockBox: LockBox<L>) => Promise<T>
]): Promise<T>;
withMultiF<T>(...params: [
...requests: Array<MultiLockRequest<L>>,
f: (multiLocks: Array<MultiLockAcquired<L>>) => Promise<T>
]): Promise<T>;
withG<T, TReturn, TNext>(...params: [
...requests: Array<LockRequest<L>>,
...requests: Array<MultiLockRequest<L>>,
g: (lockBox: LockBox<L>) => AsyncGenerator<T, TReturn, TNext>
]): AsyncGenerator<T, TReturn, TNext>;
withMultiG<T, TReturn, TNext>(...params: [
...requests: Array<MultiLockRequest<L>>,
g: (multiLocks: Array<MultiLockAcquired<L>>) => AsyncGenerator<T, TReturn, TNext>
]): AsyncGenerator<T, TReturn, TNext>;
}
export default LockBox;

@@ -26,34 +26,39 @@ "use strict";

const locks = [];
for (const [key, LockConstructor, ...lockingParams] of requests_) {
let lock = this._locks.get(key);
if (lock == null) {
lock = new LockConstructor();
this._locks.set(key, lock);
}
else {
// It is possible to swap the lock class, but only after the lock key is released
if (!(lock instanceof LockConstructor)) {
throw new errors_1.ErrorAsyncLocksLockBoxConflict(`Lock ${key} is already locked with class ${lock.constructor.name}, which conflicts with class ${LockConstructor.name}`);
try {
for (const [key, LockConstructor, ...lockingParams] of requests_) {
let lock = this._locks.get(key);
if (lock == null) {
lock = new LockConstructor();
this._locks.set(key, lock);
}
}
const lockAcquire = lock.lock(...lockingParams);
let lockRelease;
try {
[lockRelease] = await lockAcquire();
}
catch (e) {
// Release all intermediate locks in reverse order
locks.reverse();
for (const [key, lockRelease, lock] of locks) {
await lockRelease();
if (!lock.isLocked()) {
this._locks.delete(key);
else {
// It is possible to swap the lock class, but only after the lock key is released
if (!(lock instanceof LockConstructor)) {
throw new errors_1.ErrorAsyncLocksLockBoxConflict(`Lock ${key} is already locked with class ${lock.constructor.name}, which conflicts with class ${LockConstructor.name}`);
}
}
throw e;
const lockAcquire = lock.lock(...lockingParams);
const [lockRelease] = await lockAcquire();
locks.push([key, lockRelease, lock]);
}
locks.push([key, lockRelease, lock]);
}
catch (e) {
// Release all intermediate locks in reverse order
locks.reverse();
for (const [key, lockRelease, lock] of locks) {
await lockRelease();
// If it is still locked, then it is held by a different context
// only delete if no contexts are locking the lock
if (!lock.isLocked()) {
this._locks.delete(key);
}
}
throw e;
}
let released = false;
return [
async () => {
if (released)
return;
released = true;
// Release all locks in reverse order

@@ -63,2 +68,4 @@ locks.reverse();

await lockRelease();
// If it is still locked, then it is held by a different context
// only delete if no contexts are locking the lock
if (!lock.isLocked()) {

@@ -73,2 +80,66 @@ this._locks.delete(key);

}
lockMulti(...requests) {
// Convert to strings
// This creates a copy of the requests
let requests_ = requests.map(([key, ...rest]) => typeof key === 'string'
? [key, key, ...rest]
: [key.toString(), key, ...rest]);
// Sort to ensure lock hierarchy
requests_.sort(([key1], [key2]) => {
// Deterministic string comparison according to 16-bit code units
if (key1 < key2)
return -1;
if (key1 > key2)
return 1;
return 0;
});
// Avoid duplicate locking
requests_ = requests_.filter(([key], i, arr) => i === 0 || key !== arr[i - 1][0]);
const lockAcquires = [];
for (const [key, keyOrig, LockConstructor, ...lockingParams] of requests_) {
const lockAcquire = async () => {
let lock = this._locks.get(key);
let lockRelease;
try {
if (lock == null) {
lock = new LockConstructor();
this._locks.set(key, lock);
}
else {
// It is possible to swap the lock class, but only after the lock key is released
if (!(lock instanceof LockConstructor)) {
throw new errors_1.ErrorAsyncLocksLockBoxConflict(`Lock ${key} is already locked with class ${lock.constructor.name}, which conflicts with class ${LockConstructor.name}`);
}
}
const lockAcquire = lock.lock(...lockingParams);
[lockRelease] = await lockAcquire();
}
catch (e) {
// If it is still locked, then it is held by a different context
// only delete if no contexts are locking the lock
if (!lock.isLocked()) {
this._locks.delete(key);
}
throw e;
}
let released = false;
return [
async () => {
if (released)
return;
released = true;
await lockRelease();
// If it is still locked, then it is held by a different context
// only delete if no contexts are locking the lock
if (!lock.isLocked()) {
this._locks.delete(key);
}
},
lock,
];
};
lockAcquires.push([keyOrig, lockAcquire, ...lockingParams]);
}
return lockAcquires;
}
get locks() {

@@ -118,2 +189,8 @@ return this._locks;

}
async withMultiF(...params) {
const f = params.pop();
const lockAcquires = this.lockMulti(...params);
const lockAcquires_ = lockAcquires.map(([key, lockAcquire, ...lockingParams]) => (...r) => lockAcquire(...r).then(([lockRelease, lock]) => [lockRelease, [key, lock, ...lockingParams]]));
return (0, resources_1.withF)(lockAcquires_, f);
}
withG(...params) {

@@ -123,4 +200,10 @@ const g = params.pop();

}
withMultiG(...params) {
const g = params.pop();
const lockAcquires = this.lockMulti(...params);
const lockAcquires_ = lockAcquires.map(([key, lockAcquire, ...lockingParams]) => (...r) => lockAcquire(...r).then(([lockRelease, lock]) => [lockRelease, [key, lock, ...lockingParams]]));
return (0, resources_1.withG)(lockAcquires_, g);
}
}
exports.default = LockBox;
//# sourceMappingURL=LockBox.js.map

@@ -15,3 +15,3 @@ import type { MutexInterface } from 'async-mutex';

protected _writerCount: number;
lock(type: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockReader>;
lock(type?: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockReader>;
read(timeout?: number): ResourceAcquire<RWLockReader>;

@@ -18,0 +18,0 @@ write(timeout?: number): ResourceAcquire<RWLockReader>;

@@ -18,3 +18,3 @@ "use strict";

}
lock(type, timeout) {
lock(type = 'write', timeout) {
switch (type) {

@@ -67,4 +67,8 @@ case 'read':

}
let released = false;
return [
async () => {
if (released)
return;
released = true;
readersRelease = await this.readersLock.acquire();

@@ -99,4 +103,8 @@ const readerCount = --this._readerCount;

}
let released = false;
return [
async () => {
if (released)
return;
released = true;
release();

@@ -103,0 +111,0 @@ --this._writerCount;

@@ -15,3 +15,3 @@ import type { MutexInterface } from 'async-mutex';

protected _writerCount: number;
lock(type: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockWriter>;
lock(type?: 'read' | 'write', timeout?: number): ResourceAcquire<RWLockWriter>;
read(timeout?: number): ResourceAcquire<RWLockWriter>;

@@ -18,0 +18,0 @@ write(timeout?: number): ResourceAcquire<RWLockWriter>;

@@ -19,3 +19,3 @@ "use strict";

}
lock(type, timeout) {
lock(type = 'write', timeout) {
switch (type) {

@@ -71,4 +71,8 @@ case 'read':

}
let released = false;
return [
async () => {
if (released)
return;
released = true;
const readerCount = --this._readerCount;

@@ -117,4 +121,8 @@ // The last reader unlocks

}
let released = false;
return [
async () => {
if (released)
return;
released = true;
this.readersRelease();

@@ -121,0 +129,0 @@ writersRelease();

@@ -22,3 +22,3 @@ import type { ResourceAcquire } from '@matrixai/resources';

}
declare type LockRequest<L extends Lockable = Lockable> = [
declare type MultiLockRequest<L extends Lockable = Lockable> = [
key: ToString,

@@ -28,2 +28,12 @@ lockConstructor: new () => L,

];
export type { POJO, ToString, Lockable, LockRequest };
declare type MultiLockAcquire<L extends Lockable = Lockable> = [
key: ToString,
lockAcquire: ResourceAcquire<L>,
...lockingParams: Parameters<L['lock']>
];
declare type MultiLockAcquired<L extends Lockable = Lockable> = [
key: ToString,
lock: L,
...lockingParams: Parameters<L['lock']>
];
export type { POJO, ToString, Lockable, MultiLockRequest, MultiLockAcquire, MultiLockAcquired, };
{
"name": "@matrixai/async-locks",
"version": "2.3.1",
"version": "3.0.0",
"author": "Roger Qiu",

@@ -5,0 +5,0 @@ "description": "Asynchronous locking utilities",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc