Note This Developer Guide is Work in Progress.
The library libprocess provides high level elements for an actor programming style with asynchronous message-handling and a variety of related basic system primitives. It's API and implementation are written in C++.
The design of libprocess is inspired by Erlang, a language that implements the actor model.
As the name already suggests, one of the libprocess core concepts is a Process. This is a single threaded, independent actor which can communicate with other processes by sending and receiving messages. These are serialized into Protobuf messages format and stored in the recipient process' message buffer, from where its thread can process them in a serial fashion. In order to always be responsive processes should avoid blocking at all costs.
A process can be identified symbolically by its PID.
Basic communication between processes is supported by send, route, and install.
At a higher level, functional composition of interactions between processes is facilitated by the concept
of a Future and a Promise. A Future
is a
read-only placeholder for a result which might be computed
asynchronously. A Promise on the other side is a
handle providing write access to a referenced Future
.
The following primitives compose closures with future results: delay, defer, and dispatch. This gives rise to the pattern of future chaining.
This is one of the major patterns which combine the main concepts of libprocess.
Async defines a function template for asynchronously executing function closures. It provides their results as Futures.
defer
allows the caller to postpone the decision wether to dispatch something by creating a callable object which can perform the dispatch at a later point in time.
delay
instead of dispatching for execution right away, it allows it to be scheduled after a certain time duration.
dispatch
schedules a method for asynchronous execution.
The libprocess futures mimic futures in other languages like Scala. It is a placeholder for a future value which is not (necessarily) ready yet. A future in libprocess is a C++ template which is specialized for the return type, for example Try. A future can either be: ready (carrying along a value which can be extracted with .get()), failed (in which case .error() will encode the reason for the failure) or discarded.
Futures can be created in numerous ways: awaiting the result of a method call with defer, dispatch, and delay or as the read-end of a promise.
Future::then
allows to invoke callbacks once a future is completed.
using namespace process;
int main(int argc, char** argv)
{
...;
Future<int> i = dispatch(process, &QueueProcess<int>::dequeue);
dispatch(process, &QueueProcess<int>::enqueue, 42);
i.then([] (int i) {
// Use 'i'.
});
...;
}
Generates a unique identifier string given a prefix. This is used to
provide PID
names.
A PID
provides a level of indirection for naming a process without
having an actual reference (pointer) to it (necessary for remote
processes).
A process
is an actor, effectively a cross between a thread and an object.
Creating/spawning a process is very cheap (no actual thread gets created, and no thread stack gets allocated).
Each process has a queue of incoming events that it processes one at a time.
Processes provide execution contexts (only one thread executing within a process at a time so no need for per process synchronization).
A promise
is an object that can fulfill a futures, i.e. assign a result value to it.
route
installs an http endpoint onto a process.
TODO: ADD PATTERNS