How Programs Run
You can write a lot of ƿit knowing only that actors send each other messages. This page covers the rest of what the machinery does on your behalf, so the behaviour you see has an explanation.
Starting
Running a program creates an actor. Before your program body executes, the runtime runs every module in the executable’s closure — each one once, in dependency order, so that a module’s imports have already produced their values by the time it runs.
Each module’s returned value is stone, and within one executable it is shared: if three
modules all use('config'), all three receive the same value. A second actor running the
same executable repeats the whole process and gets its own values.
Then your program body runs. Typically it registers a receiver and waits.
Turns
An actor handles one message at a time. Processing one message is a turn, and a turn runs to completion: no other message is interleaved into it, and no other actor sees your half-finished state. You never need a lock to protect an actor’s own data.
Turns are why callbacks matter. Work that finishes later — a network read, a delay, a message to another actor — cannot block the turn waiting for an answer, so it hands you the answer in a later turn through a callback. See Functions.
A turn can be suspended and resumed. If a turn runs long, the scheduler may pause it and let other actors run, then continue it exactly where it stopped. Suspension is invisible to your code: locals, position, and partial work are all preserved.
Sending messages
send(actor, message) delivers a message to an actor. It arrives, and the actor handles
it in one of its turns.
Where that actor is does not change how you write the code. The same send reaches an
actor in the same process, in another process, or on another machine — the runtime encodes
the message, moves it, and decodes it on the other side. What you send is copied, so both
sides hold their own values and neither can reach into the other’s memory.
To reply, send back to the message itself:
$receiver(function(message) {
send(message, {status: "ok"})
})
The message carries the routing needed to find the sender.
Slow actors
An actor that will not yield is a problem for everyone sharing the machine, so the runtime watches for it. A turn that overruns its time is recorded as a slow strike, and a slow actor goes to the back of the line — the system stays responsive by serving everyone else first. After enough strikes the runtime may remove the actor for being abusive.
Ordinary code, even code doing substantial work, is suspended and resumed rather than struck: suspension is the normal response to a long turn. Strikes accumulate for actors that will not yield at all.
Both the threshold and what happens at it are settable — per actor, and differently for guest actors than for your own. A shop shipped with an application can be configured to be lenient, strict, or to never remove an actor at all; see the shop you ship.
Stopping
$stop() stops the current actor; $stop(child) stops one it started. Stopping releases
the actor’s heap, its pending messages, and everything it held.
Actors can be coupled so that one dying takes the other with it. A child is
automatically coupled to the actor that started it, so a supervising actor going away does
not leave orphans behind. $couple(other) sets this up explicitly.