javascript - Implementando um bloqueio recursivo
Eu criei um mixin sincronizável, que fornece a função 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());
}
}
}
}
}
Mas o bloqueio não é recursivo, bloquear o objeto quando já estiver bloqueado causará deadlock:
const c = new Synchronizable();
await c.synchronized(() => c.synchronized(async () => void 0));
Como implementar travamento recursivo?
O código completo está enviado para o GitHub com casos de teste
Primeiro pensamento
Assim como em qualquer outra linguagem, salve o id da thread atual ao bloquear, depois compare o id da thread salvo com o id da thread atual; se coincidir, prossiga.
Mas o JavaScript não fornece id de thread, e adiar um closure não gera um novo id.
Segundo pensamento
rastreie a pilha de chamadas, encontre qualquer outra chamada de lock dentro da pilha, verifique se é o mesmo lock.
O problema é que o rastreamento de pilha pode não seguir o callback, como setTimeout, então ele não será capaz de detectar um bloqueio antes do callback.
Resposta #1
Descobri que é possível alcançar isso com Zone.js usando a primeira abordagem, onde o Zone.js fornece uma maneira de definir uma variável local de thread.