Documentation Home >> Headers >> <jss/actor.hpp> Header

The <jss/actor.hpp> provides the jss::actor and jss::actor_ref classes for controlling and referencing actors.

namespace jss
{
    class stop_actor;
    class no_actor;
    class actor;
    class actor_ref;
}

Using Actors provides an easy way of structuring your application to use multiple threads without worrying about the details of mutexes, condition variables and other low-level facilities. Each jss::actor runs its task (which can be a function or callable object) on its own thread, and has an associated message queue.

Actors then send messages to each other either by calling jss::actor::send() directly on an jss::actor object, or on an jss::actor_ref object. Messages can be of any copyable or movable type, so you can define your messages in whatever way is most appropriate. The jss::actor::self() function provides an easy way to pass around a reference to the current actor. You can use this for passing a reference to the current actor as part of a message, in order for the receiving actor to send a response.

Messages are received by calling jss::actor::receive(). Calling jss::actor::receive() on its own will just receive and discard all messages (as no matches are specified) until a jss::stop_actor message is received (in which case a jss::stop_actor exception is thrown). In order to process the messages you need to chain a call to match on the jss::actor::receive() call, specifying the type of message to match, and the function to call to handle it. This could be a lambda:

jss::actor::receive().match<my_message>([](my_message){
    std::cout<<"my message received"<<std::endl;
    });

This will receive and discard all messages until either a my_message message is received, in which case the lambda is run to print "my message received" on standard output, or a jss::stop_actor message is received. Once a message has been handled, jss::actor::receive() returns to its caller. If you wish to handle more than one message, just put the jss::actor::receive() call in a loop.

You can also specify that you wish to process one of several types of message, whichever arrives first. This can be done by chaining multiple match calls:

jss::actor::receive()
    .match<first_message>([](first_message){
        std::cout<<"first message type received"<<std::endl;
    })
    .match<second_message>([](second_message){
        std::cout<<"second message type received"<<std::endl;
    });

This time, jss::actor::receive() will discard all messages until it receives either a message of type first_message or one of type second_message (or a jss::stop_actor message). Again, it returns as soon as one message has been processed, whichever type it is. Any number of match calls can be chained in this way, and the jss::actor::receive() call will block until one of the specified messages has been received.

Example

Here is a complete example of actors playing ping-pong: each actor waits in a loop for a pingpong message. When it receives the message it either prints "ping" or "pong", and sends a message back to the other actor. After 5 seconds the main thread stops both the actors.

Note how the message type is just a simple struct; messages do not need to derive from a base class or implement a specific interface, they just need to be move-constructible or copy-constructible.

Also, see how jss::actor::self() is used to pass references between actors, without either actor needing to know the identity of the other.

#include <jss/actor.hpp>
#include <iostream>
#include <thread>

int main()
{
    struct pingpong
    {
        jss::actor_ref sender;

        pingpong(jss::actor_ref sender_):
            sender(sender_)
        {}
    };

    jss::actor pp1(
        []{
            for(;;)
            {
                jss::actor::receive().match<pingpong>(
                    [](pingpong p){
                        std::cout<<"ping\n";
                        p.sender.send(pingpong(jss::actor::self()));
                    });
            }
        });
    jss::actor pp2(
        []{
            for(;;)
            {
                jss::actor::receive().match<pingpong>(
                    [](pingpong p){
                        std::cout<<"pong\n";
                        p.sender.send(pingpong(jss::actor::self()));
                    });
            }
        });

    pp1.send(pingpong(pp2));

    std::this_thread::sleep_for(std::chrono::seconds(2));
    pp1.stop();
    pp2.stop();
}
See Also