I have created a Synchronizable mixin, which provides synchronized function:
const lock = Symbol('Synchronizable lock');
const queue = Symbol('Synchronizable queue');
export class Synchronizable {
private [lock] = false;
private [queue]: Array<() => void> = [];
public async synchronized<T>(fn: () => Promise<T>): Promise<T> {
while (true) {
if (this[lock]) await new Promise(resolve => this[queue].push(resolve));
else {
this[lock] = true;
try {
return await fn();
} finally {
this[lock] = false;
const tmp = this[queue];
this[queue] = [];
tmp.forEach(e => e());
}
}
}
}
}
But the lock is not recursive, locking the object when locked will cause dead lock:
const c = new Synchronizable();
await c.synchronized(() => c.synchronized(async () => void 0));
How to implement recursive lock ?
The full code is upload to github with testcases
First thought
Just like any other language, save current thread-id when lock then compare the saved thread-id with current thread-id, if match proceed.
But javascript doesn't provide thread-id, and defer a closure doesn't generate a new id.
Second thought
track the call stack, find any other lock call inside stack, check if it is the same lock.
The problem is that stack trace may not follow callback, like setTimeout, so it won't be able to detect a lock before the callback.