just::thread Pro C++ Thread Library Extensions

Just::Thread Pro 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.

It also includes support for the Concurrency TS including atomic_shared_ptr and continuations.

For more information, please see below for an overview, or the full documentation.

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

Features

Overview

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.

The free function jss::apply can also be used to perform a function on the wrapped value(s) of one or more objects. The function is called with the locks on all supplied objects held; the locks are released when the function returns or throws an exception.

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

void foo(){
    *s="hello";
    *s2=" world";

    jss::apply([](std::string& s,std::string & s2){
        s += s2;
    },s,s2);
}

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

Latches and Barriers

std::experimental::latch, std::experimental::barrier and std::experimental::flex_barrier are features of the Concurrency TS. std::experimental::latch is a single-use countdown latch which allows you to wait for a set of threads to signal the latch, whereas std::experimental::barrier and std::experimental::flex_barrier are reusable barriers that allow a set of threads to rendezvous at a particular point and only proceed when they are all ready. These can be great for synchronizing batch operations or initialization steps.

Additional Benefits