Я создал синхронизируемый миксин, который обеспечивает функцию synchronized
:
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());
}
}
}
}
}
Но блокировка не является рекурсивной, блокировка объекта при блокировке приведет к мертвой блокировке:
const c = new Synchronizable();
await c.synchronized(() => c.synchronized(async () => void 0));
Как реализовать рекурсивную блокировку?
Полный код загружается на github с тестовыми примерами
Первая мысль
Как и на любом другом языке, сохранитетекущий идентификатор потока при блокировке, затем сравните сохраненный идентификатор потока с текущим идентификатором потока, если сопоставление продолжается.
Но javascript не предоставляет идентификатор потока и откладывает закрытие не генерирует новый идентификатор.
Вторая мысль
отслеживать стек вызовов, находить любые другие вызовы блокировки внутри стека, проверять, является ли это такой же блокировкой.
Проблема в том, что трассировка стека может невыполните обратный вызов, например setTimeout
, поэтому он не сможет обнаружить блокировку до обратного вызова.