Skip to content

Commit d50ac51

Browse files
committed
WIP
1 parent 511a742 commit d50ac51

File tree

3 files changed

+81
-64
lines changed

3 files changed

+81
-64
lines changed

src/LockBox.ts

+65-60
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import type { ResourceAcquire, ResourceRelease } from '@matrixai/resources';
2-
import type { Lockable, ToString, LockRequest } from './types';
2+
import type {
3+
ToString,
4+
Lockable,
5+
MultiLockRequest,
6+
MultiLockAcquire,
7+
MultiLockAcquired
8+
} from './types';
39
import { withF, withG } from '@matrixai/resources';
410
import { ErrorAsyncLocksLockBoxConflict } from './errors';
511

612
class LockBox<L extends Lockable = Lockable> implements Lockable {
713
protected _locks: Map<string, L> = new Map();
814

9-
public lock(...requests: Array<LockRequest<L>>): ResourceAcquire<LockBox<L>> {
15+
public lock(...requests: Array<MultiLockRequest<L>>): ResourceAcquire<LockBox<L>> {
1016
return async () => {
1117
// Convert to strings
1218
// This creates a copy of the requests
@@ -79,8 +85,8 @@ class LockBox<L extends Lockable = Lockable> implements Lockable {
7985
}
8086

8187
public lockMulti(
82-
...requests: Array<LockRequest<L>>
83-
): Array<[ToString, ResourceAcquire<L>]> {
88+
...requests: Array<MultiLockRequest<L>>
89+
): Array<MultiLockAcquire<L>> {
8490
// Convert to strings
8591
// This creates a copy of the requests
8692
let requests_: Array<
@@ -101,51 +107,49 @@ class LockBox<L extends Lockable = Lockable> implements Lockable {
101107
requests_ = requests_.filter(
102108
([key], i, arr) => i === 0 || key !== arr[i - 1][0],
103109
);
104-
const lockAcquires: Array<[ToString, ResourceAcquire<L>]> = [];
110+
const lockAcquires: Array<MultiLockAcquire<L>> = [];
105111
for (const [key, keyOrig, LockConstructor, ...lockingParams] of requests_) {
106-
lockAcquires.push([
107-
keyOrig,
108-
async () => {
109-
let lock = this._locks.get(key);
110-
let lockRelease: ResourceRelease;
111-
try {
112-
if (lock == null) {
113-
lock = new LockConstructor();
114-
this._locks.set(key, lock);
115-
} else {
116-
// It is possible to swap the lock class, but only after the lock key is released
117-
if (!(lock instanceof LockConstructor)) {
118-
throw new ErrorAsyncLocksLockBoxConflict(
119-
`Lock ${key} is already locked with class ${lock.constructor.name}, which conflicts with class ${LockConstructor.name}`,
120-
);
121-
}
112+
const lockAcquire: ResourceAcquire<L> = async () => {
113+
let lock = this._locks.get(key);
114+
let lockRelease: ResourceRelease;
115+
try {
116+
if (lock == null) {
117+
lock = new LockConstructor();
118+
this._locks.set(key, lock);
119+
} else {
120+
// It is possible to swap the lock class, but only after the lock key is released
121+
if (!(lock instanceof LockConstructor)) {
122+
throw new ErrorAsyncLocksLockBoxConflict(
123+
`Lock ${key} is already locked with class ${lock.constructor.name}, which conflicts with class ${LockConstructor.name}`,
124+
);
122125
}
123-
const lockAcquire = lock.lock(...lockingParams);
124-
[lockRelease] = await lockAcquire();
125-
} catch (e) {
126+
}
127+
const lockAcquire = lock.lock(...lockingParams);
128+
[lockRelease] = await lockAcquire();
129+
} catch (e) {
130+
// If it is still locked, then it is held by a different context
131+
// only delete if no contexts are locking the lock
132+
if (!lock!.isLocked()) {
133+
this._locks.delete(key);
134+
}
135+
throw e;
136+
}
137+
let released = false;
138+
return [
139+
async () => {
140+
if (released) return;
141+
released = true;
142+
await lockRelease();
126143
// If it is still locked, then it is held by a different context
127144
// only delete if no contexts are locking the lock
128145
if (!lock!.isLocked()) {
129146
this._locks.delete(key);
130147
}
131-
throw e;
132-
}
133-
let released = false;
134-
return [
135-
async () => {
136-
if (released) return;
137-
released = true;
138-
await lockRelease();
139-
// If it is still locked, then it is held by a different context
140-
// only delete if no contexts are locking the lock
141-
if (!lock!.isLocked()) {
142-
this._locks.delete(key);
143-
}
144-
},
145-
lock,
146-
];
147-
},
148-
]);
148+
},
149+
lock,
150+
];
151+
};
152+
lockAcquires.push([keyOrig, lockAcquire, ...lockingParams]);
149153
}
150154
return lockAcquires;
151155
}
@@ -194,71 +198,72 @@ class LockBox<L extends Lockable = Lockable> implements Lockable {
194198

195199
public async withF<T>(
196200
...params: [
197-
...requests: Array<LockRequest<L>>,
201+
...requests: Array<MultiLockRequest<L>>,
198202
f: (lockBox: LockBox<L>) => Promise<T>,
199203
]
200204
): Promise<T> {
201205
const f = params.pop() as (lockBox: LockBox<L>) => Promise<T>;
202206
return withF(
203-
[this.lock(...(params as Array<LockRequest<L>>))],
207+
[this.lock(...(params as Array<MultiLockRequest<L>>))],
204208
([lockBox]) => f(lockBox),
205209
);
206210
}
207211

208212
public async withMultiF<T>(
209213
...params: [
210-
...requests: Array<LockRequest<L>>,
211-
f: (multiLocks: Array<[ToString, L]>) => Promise<T>,
214+
...requests: Array<MultiLockRequest<L>>,
215+
f: (multiLocks: Array<MultiLockAcquired<L>>) => Promise<T>,
212216
]
213217
): Promise<T> {
214-
const f = params.pop() as (multiLocks: Array<[ToString, L]>) => Promise<T>;
215-
const lockAcquires = this.lockMulti(...(params as Array<LockRequest<L>>));
216-
const lockAcquires_: Array<ResourceAcquire<[ToString, L]>> =
218+
const f = params.pop() as (multiLocks: Array<MultiLockAcquired<L>>) => Promise<T>;
219+
const lockAcquires = this.lockMulti(...(params as Array<MultiLockRequest<L>>));
220+
221+
const lockAcquires_: Array<ResourceAcquire<MultiLockAcquired<L>>> =
217222
lockAcquires.map(
218-
([key, lockAcquire]) =>
223+
([key, lockAcquire, ...lockingParams]) =>
219224
(...r) =>
220225
lockAcquire(...r).then(
221226
([lockRelease, lock]) =>
222-
[lockRelease, [key, lock]] as [ResourceRelease, [ToString, L]],
227+
[lockRelease, [key, lock, ...lockingParams]] as [ResourceRelease, MultiLockAcquired<L>],
223228
),
224229
);
225230
return withF(lockAcquires_, f);
226231
}
227232

228233
public withG<T, TReturn, TNext>(
229234
...params: [
230-
...requests: Array<LockRequest<L>>,
235+
...requests: Array<MultiLockRequest<L>>,
231236
g: (lockBox: LockBox<L>) => AsyncGenerator<T, TReturn, TNext>,
232237
]
233238
): AsyncGenerator<T, TReturn, TNext> {
234239
const g = params.pop() as (
235240
lockBox: LockBox<L>,
236241
) => AsyncGenerator<T, TReturn, TNext>;
237242
return withG(
238-
[this.lock(...(params as Array<LockRequest<L>>))],
243+
[this.lock(...(params as Array<MultiLockRequest<L>>))],
239244
([lockBox]) => g(lockBox),
240245
);
241246
}
242247

243248
public withMultiG<T, TReturn, TNext>(
244249
...params: [
245-
...requests: Array<LockRequest<L>>,
250+
...requests: Array<MultiLockRequest<L>>,
246251
g: (
247-
multiLocks: Array<[ToString, L]>,
252+
multiLocks: Array<MultiLockAcquired<L>>,
248253
) => AsyncGenerator<T, TReturn, TNext>,
249254
]
250255
) {
251256
const g = params.pop() as (
252-
multiLocks: Array<[ToString, L]>,
257+
multiLocks: Array<MultiLockAcquired<L>>,
253258
) => AsyncGenerator<T, TReturn, TNext>;
254-
const lockAcquires = this.lockMulti(...(params as Array<LockRequest<L>>));
255-
const lockAcquires_: Array<ResourceAcquire<[ToString, L]>> =
259+
const lockAcquires = this.lockMulti(...(params as Array<MultiLockRequest<L>>));
260+
const lockAcquires_: Array<ResourceAcquire<MultiLockAcquired<L>>> =
256261
lockAcquires.map(
257-
([key, lockAcquire]) =>
262+
([key, lockAcquire, ...lockingParams]) =>
258263
(...r) =>
259264
lockAcquire(...r).then(
260265
([lockRelease, lock]) =>
261-
[lockRelease, [key, lock]] as [ResourceRelease, [ToString, L]],
266+
[lockRelease, [key, lock, ...lockingParams]] as [ResourceRelease, MultiLockAcquired<L>],
262267
),
263268
);
264269
return withG(lockAcquires_, g);

src/types.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,22 @@ interface Lockable {
2323
): AsyncGenerator<T, TReturn, TNext>;
2424
}
2525

26-
type LockRequest<L extends Lockable = Lockable> = [
26+
type MultiLockRequest<L extends Lockable = Lockable> = [
2727
key: ToString,
2828
lockConstructor: new () => L,
2929
...lockingParams: Parameters<L['lock']>,
3030
];
3131

32-
export type { POJO, ToString, Lockable, LockRequest };
32+
type MultiLockAcquire<L extends Lockable = Lockable> = [
33+
key: ToString,
34+
lockAcquire: ResourceAcquire<L>,
35+
...lockingParams: Parameters<L['lock']>,
36+
];
37+
38+
type MultiLockAcquired<L extends Lockable = Lockable> = [
39+
key: ToString,
40+
lock: L,
41+
...lockingParams: Parameters<L['lock']>,
42+
];
43+
44+
export type { POJO, ToString, Lockable, MultiLockRequest, MultiLockAcquire, MultiLockAcquired };

tests/LockBox.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ResourceRelease } from '@matrixai/resources';
2-
import type { LockRequest } from '@/types';
2+
import type { MultiLockRequest } from '@/types';
33
import { withF, withG } from '@matrixai/resources';
44
import LockBox from '@/LockBox';
55
import Lock from '@/Lock';
@@ -339,7 +339,7 @@ describe(LockBox.name, () => {
339339
test('can map keys to LockBox locks', async () => {
340340
const lockBox = new LockBox();
341341
const keys = ['1', '2', '3', '4'];
342-
const locks: Array<LockRequest<RWLockWriter>> = keys.map((key) => [
342+
const locks: Array<MultiLockRequest<RWLockWriter>> = keys.map((key) => [
343343
key,
344344
RWLockWriter,
345345
'write',

0 commit comments

Comments
 (0)