actor
stdlib/actor.hk: actor-runtime types surfaced to user code.
Every send picks up [throws actor.SendFailed] (HANKI.md §15). The runtime materialises a SendFailed at the send site: MailboxFull when a bounded mailbox is full, Died(ActorId, DeathCause) when the target has terminated, Timeout when actor.await_timeout! gave up waiting. ActorId and DeathCause are also delivered to a supervisor's on actor_died handler.
Bounded await, a compiler special form like the send forms and no declarable fn, since Future<T> cannot cross a fn boundary):
actor.await_timeout!(f, ms) # f: Future<T>, ms: i32 -> T
It types as await f does and raises the future's effects at the call site; if no reply lands within ms milliseconds it throws SendFailed.Timeout instead. The late reply, if one ever arrives, is abandoned: the handler still ran, and only the wait was bounded. Under --deterministic the deadline is virtual, on the gate's clock, and a timeout costs no real time and replays per seed.
Arriving here for something this module does not hold
- Stopping an actor.
actor.shutdown!(target)is a compiler special form and nodef, and it has no page of its own here;Shutdownbelow is what it answers, andExplicitShutdownabove is the death cause it raises. It also reaches a child process or a long database call the target is blocked in, andextra/processandextra/sqliteboth point back at it. - Restart policy.
extra/supervisorhas it. This module gives a supervisor the facts it decides on -ActorId,DeathCause, and theon actor_diedhandler they arrive through, and stops there. - Spawning and sending. Also special forms (
spawn,c.op(),await), and they are in HANKI.md §15 and not in this file. What is here is the vocabulary those forms fail with.
SendFailed
type SendFailed
MailboxFull
Died(ActorId, DeathCause)
Timeout
end
Send-failure throw raised at the call site of any blocking send, statement-position fire-and-forget, or matching await.
try c.op() catch e: actor.SendFailed match e MailboxFull -> retry() Died(, ) -> crash!("peer gone") Timeout -> retry() end end
DeathCause
type DeathCause
UncaughtThrow(string, string)
InvariantViolation(string, Option<string>, string)
HostPanic(string)
ExplicitShutdown
Gone
end
Cause of an actor's termination. Delivered to the supervisor as the second argument of on actor_died(who: ActorId, cause: DeathCause) and carried as the Died payload at send-site throws. Every variant below is one the runtime produces.
impl Display<DeathCause>
to_string
def to_string(self) -> string
The same one-line wording the runtime writes to stderr when a death escalates to root, and a supervisor that renders the cause itself and an unsupervised crash describe the same event identically.
Gone.to_string() => "gone (actor id recycled)"
impl Display<SendFailed>
to_string
def to_string(self) -> string
Names the failure a catch e: actor.SendFailed arm caught. Died reports the target's name and not its raw id: the id is a packed slot-and-generation word, noise beside a name the program chose.
Timeout.to_string() => "reply timed out"
ActorId
opaque ActorId
raw: u64
name: string
end
Opaque identifier for a (possibly-dead) actor. The runtime constructs ActorId values on the supervisor-delivery and send-site throw paths; user code never fabricates one.
Shutdown
type Shutdown
Terminated
AlreadyDead
SelfScheduled
end
Outcome of actor.shutdown!(target), read by awaiting the returned Future<Shutdown>. Every outcome is normal, the target ending up down in each case, and the future therefore has no throw and is freely discardable for fire-and-forget. The runtime constructs these values.