Skip to content

Commit

Permalink
fix: 🐛 limit mutex scope to a single class instance
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Mar 19, 2024
1 parent 839c898 commit c994280
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
34 changes: 34 additions & 0 deletions src/__tests__/mutex.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {mutex} from '../mutex';
import {tick} from '../tick';

test('can execute code sequentially', async () => {
const code = async () => 123;
Expand Down Expand Up @@ -52,3 +53,36 @@ test('passes through arguments in the decorated method', async () => {
const res2 = await Promise.all([test.code(10), test.code(10), test.code(10), test.code(10)]);
expect(res2).toStrictEqual([0.1, 0.1, 0.1, 0.1]);
});

test('is applied per method', async () => {
class Test {
@mutex async echo(value: any) {
await tick(1);
return value;
}
@mutex async echo2(value: any) {
await tick(1);
return value;
}
}
const a = new Test();
const p1 = a.echo(1);
const p2 = a.echo2(2);
expect(await p1).toBe(1);
expect(await p2).toBe(2);
});

test('is applied per instance', async () => {
class Test {
@mutex async echo(value: any) {
await tick(1);
return value;
}
}
const a1 = new Test();
const a2 = new Test();
const p1 = a1.echo(1);
const p2 = a2.echo(2);
expect(await p1).toBe(1);
expect(await p2).toBe(2);
});
16 changes: 13 additions & 3 deletions src/mutex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ import {codeMutex} from './codeMutex';
* returns the result of the ongoing execution.
*/
export function mutex<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Promise<Return>,
fn: (this: This, ...args: Args) => Promise<Return>,
context?: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Promise<Return>>,
) {
const mut = codeMutex<Return>();
const isDecorator = !!context;
if (!isDecorator) {
const mut = codeMutex<Return>();
return async function (this: This, ...args: Args): Promise<Return> {
return await mut(async () => await fn.call(this, ...args));
};
}
const instances = new WeakMap<any, WeakMap<any, any>>();
return async function (this: This, ...args: Args): Promise<Return> {
return await mut(async () => await target.call(this, ...args));
let map = instances.get(this);
if (!map) instances.set(this, map = new WeakMap<any, any>());
if (!map.has(fn)) map.set(fn, codeMutex<Return>());
return await map.get(fn)!(async () => await fn.call(this, ...args));
};
}

0 comments on commit c994280

Please sign in to comment.