Fibras
Um stream action suspenso em cada emit — corrotina de verdade, com a limitação sem pilha dita com esse nome.
Uma fibra aqui não é aproximação. Um stream action da linguagem já é um gerador do Python, e o interpretador suspende o corpo dele em cada emit. O escalonador dirige esse gerador: o valor emitido diz o que a fibra está esperando, e a troca de contexto é o quadro do gerador.
adopt Arcane.Laco as L
stream action trabalhador(nome, diario):
cycle i from 1 to 3:
diario.append($"{nome}{i}")
emit L.ceder()
laco := L.novo()
diario := []
L.fibra(laco, trabalhador, ["A", diario])
L.fibra(laco, trabalhador, ["B", diario])
assert L.fibras(laco) is 2
L.rodar(laco)
// intercaladas: e isto que prova o escalonamento cooperativo
assert diario is ["A1", "B1", "A2", "B2", "A3", "B3"]
assert L.fibras(laco) is 0Se fossem sequenciais o diário seria A1 A2 A3 B1 B2 B3. Elas se intercalam porque cada emit devolve o controle ao escalonador, que escolhe a próxima.
O que uma fibra pode esperar#
| Pedido | Retoma quando |
|---|---|
L.ceder() | na próxima volta — cede sem esperar nada |
L.dormir(ms) | passado o prazo, sem prender thread nenhuma |
L.depois_de(ms, caixa, chave, valor) | passado o prazo, com o valor já na caixa |
L.ler(soquete, caixa, chave) | houver o que ler; o dado chega pela caixa |
L.escrever(soquete) | der para escrever sem bloquear |
L.esperar(outra) | a outra fibra terminar |
A lista é fechada: um vault qualquer emitido por engano vira erro com a lista, e não uma fibra parada para sempre esperando algo que ninguém registrou.
A caixa, e por que ela existe#
emit é instrução, não expressão: ele não devolve valor para a fibra. Então o laço entrega por um vault que a fibra passou — a caixa. Ser explícito aqui é melhor que fingir o contrário.
adopt Arcane.Laco as L
stream action espera(caixa):
emit L.depois_de(20, caixa, "resposta", "chegou")
caixa["visto"] := caixa["resposta"]
laco := L.novo()
caixa := {"resposta": void, "visto": void}
L.fibra(laco, espera, [caixa])
L.rodar(laco)
assert caixa["visto"] is "chegou"Sem pilha — e isso tem nome#
Cancelar#
adopt Arcane.Laco as L
stream action longa(diario):
diario.append("comecei")
emit L.dormir(50)
diario.append("nao chega aqui")
laco := L.novo()
diario := []
f := L.fibra(laco, longa, [diario])
L.apos(laco, 5, lambda => L.cancelar(f))
L.rodar(laco)
assert diario is ["comecei"]
assert L.cancelada(f)Cancelar uma fibra fecha o gerador — o que roda os defer do corpo, como um halt faria. Uma thread do sistema não se cancela assim: é a vantagem concreta de a fibra ser um objeto e não um recurso do SO.