/** * Parte 5 — Condição de corrida sem threads. * * npx tsx src/12-corrida-logica.ts * * Uma thread só garante que um trecho síncrono não é interrompido. Ela não * garante nada entre dois trechos: cada `await` é um ponto em que outra * chamada pode entrar, ler o mesmo estado e decidir com base nele. * * O acervo aqui tem um exemplar por livro e vive na memória do processo. */ import { cronometro, esperar } from './biblioteca.js'; type Situacao = 'DISPONIVEL' | 'RESERVADO' | 'EMPRESTADO'; const situacoes = new Map(); const registros: string[] = []; function reiniciarAcervo(): void { situacoes.set(2, 'DISPONIVEL'); situacoes.set(4, 'DISPONIVEL'); registros.length = 0; } /** Consulta ao cadastro de leitores: 100 ms, e rejeita leitor bloqueado. */ async function validarLeitor(leitor: string): Promise { await esperar(100); if (leitor === 'zeca') { throw new Error(`leitor ${leitor} está bloqueado`); } } // --------------------------------------------------------------------------- // Com corrida: checa, espera, marca. Entre a checagem e a marcação há um // await — e é por ali que a segunda chamada entra. // --------------------------------------------------------------------------- async function emprestarComCorrida(livroId: number, leitor: string): Promise { if (situacoes.get(livroId) !== 'DISPONIVEL') { return `${leitor}: recusado`; } await validarLeitor(leitor); // a thread atende a outra chamada aqui situacoes.set(livroId, 'EMPRESTADO'); registros.push(`livro ${livroId} → ${leitor}`); return `${leitor}: emprestado`; } // --------------------------------------------------------------------------- // Sem corrida: checar e reservar no mesmo trecho síncrono, sem await entre as // duas linhas. Só depois vem a espera; se ela falhar, a reserva é desfeita. // --------------------------------------------------------------------------- async function emprestarSemCorrida(livroId: number, leitor: string): Promise { if (situacoes.get(livroId) !== 'DISPONIVEL') { return `${leitor}: recusado`; } situacoes.set(livroId, 'RESERVADO'); // nenhum callback entra entre a checagem e esta linha try { await validarLeitor(leitor); } catch (erro) { situacoes.set(livroId, 'DISPONIVEL'); // desfaz a reserva throw erro; } situacoes.set(livroId, 'EMPRESTADO'); registros.push(`livro ${livroId} → ${leitor}`); return `${leitor}: emprestado`; } async function principal(): Promise { const marcar = cronometro(); reiniciarAcervo(); const comCorrida = await Promise.all([emprestarComCorrida(4, 'ana'), emprestarComCorrida(4, 'bruno')]); marcar(`com corrida : ${comCorrida.join(' | ')}`); marcar(`com corrida : registros = [${registros.join('; ')}]`); reiniciarAcervo(); const semCorrida = await Promise.all([emprestarSemCorrida(4, 'ana'), emprestarSemCorrida(4, 'bruno')]); marcar(`sem corrida : ${semCorrida.join(' | ')}`); marcar(`sem corrida : registros = [${registros.join('; ')}]`); // A reserva desfeita: o leitor bloqueado não prende o livro. try { await emprestarSemCorrida(2, 'zeca'); } catch (erro) { marcar(`falha : ${erro instanceof Error ? erro.message : 'erro desconhecido'} — livro 2 ${situacoes.get(2)}`); } marcar(`nova vez : ${await emprestarSemCorrida(2, 'carla')}`); } void principal();