7.3 Service Internals

Under the hood, each service record has an associated fiber, a lightweight execution thread (see Introduction in Fibers). This fiber encapsulates all the state of its corresponding service: its status (whether it’s running, stopped, etc.), its “running value” (such as the PID of its associated process), the time at which its status changed, and so on. Procedures that access the state of a service, such as service-status, or that modify it, such as start-service (see Interacting with Services), merely send a message to the service’s associated fiber.

This pattern follows the actor model: each of these per-service fibers is an actor. There are several benefits:

There are other actors in the code, such as the service registry (see Service Registry). Fibers are used pervasively throughout the code to achieve concurrency.

Note that Fibers is set up such that the shepherd process has only one POSIX thread (this is mandated by POSIX for processes that call fork, with all its warts), and fibers are scheduled in a cooperative fashion. This means that it is possible to block the shepherd process for instance by running a long computation or by waiting on a socket that is not marked as SOCK_NONBLOCK. Be careful!

We think this programming model makes the code base not only more robust, but also very fun to work with—we hope you’ll enjoy it too!