Pesquisa de site

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.