Just::Thread Pro: Actors Edition

The first release of Just::Thread Pro is here. The Actors Edition provides a framework for creating actors that run on separate threads, and communicate via message passing, as well as jss::synchronized_value for synchronizing access to a single object and jss::concurrent_map, a hash map that is safe for concurrent access from multiple threads.

See the overview below for more information, or read the full documentation.

Order your copy of just::thread Pro: Actors today, and get started within minutes.

Actors

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.

Messages are sent with the send() member function:

jss::actor a(actor_func);

a.send(my_message());

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.

FIFO queue

The jss::mpsc_fifo class template provides an unbounded FIFO queue. Multiple threads may safely add items to the queue concurrently, provided only a single thread is removing items from the queue. This is used to provide the underlying message queue for the actors.

Concurrent Hash Map

The jss::concurrent_map class template provides a hash-based map which is safe for concurrent access from multiple threads, including adding and removing elements concurrently with lookups and iteration. The interface is modelled on std::unordered_map, with an added insert_or_replace member function for replacing a stored element with another.

Synchronized access for single objects

The jss::synchronized_value class template provides synchronization for all accesses to a single object.

It is a simple wrapper around an object of the specified type, and the interface is modelled on a smart pointer, with accesses done using the pointer dereference and indirection operators.

jss::synchronized_value<std::string> s;

void foo(){
    if(s->empty()){
        *s =  "hello";
    }
}

In this example, multiple calls to foo can happen concurrently on separate threads, and all accesses to s are protected.

Buy it Now!

Order your copy of just::thread Pro: Actors today, and get started within minutes.