lockfree-async
A C++17 header-only template library for lock-free inter-thread communication and sharing of resources.
|
lockfree-async is a C++ header-only simple template library for lock-free inter-thread communication.
The fundamental building block of this library is the "IMB Freelist" multiple-producer multiple-consumer LIFO stack, as implemented by Ross Bencina in Queue World.
This library is designed to solve this problem:
Here's the general idea of how this library solves the problem:
State
the class of the object we want to share.Async<State>
.Async<State>
instance keeps a private copy of the State
objectState
object, a Getter
object can be requested to the Async<State>
instance.State
object - or, optionally, of an object of an other type that can be constructed from the State
object. The Getter
object have two methods: get
, which will return a reference to the local copy of the State
object, and update
, which will synchronize the local copy with the last available version of the State
object kept by the Async<State>
instace.Async<State>
has a method called SubmitMessage
, which can be called from any thread, that takes a std::function<void(State&)>
to asynchronously change the state of the object. Let's call a std::function<void(State&)>
a message.State
object owned by the Asyn<State>
istance.State
object kept by the getters using a lockfree FIFO stack to avoid data races. All memory allocations and deallocations are executed by the timer thread.Read on for a more detailed explanation.
Messages are used to asynchronously change the state of the State.
But what is a message? A message is a std::function<void(State&)>
.
Just put the code to change you want to execute on the State
object in a std::function<void(State&)>
and pass it to the Async<State>
instance using one of its SubmitMessage
methods.
If you need to send such messages from realtime threads, you can preallocate them using the function Async<State>::RequestMessageBuffer
.
The Messages are handled in a timer thread. You can set its period in the constructor of Async<State>
, or by calling SetTimerPeriod
.
The constructor of Async<State>
also has an optional argument of type std::function<void(State&)>
called onChange
. If not null, it will be executed on the State
object by timer callback if any message has been received.
Getter
objects can be reqeusted using the method RequestGetter<ClassToGet>
of Async<State>
. The template parameter ClassToGet
is the type that the get
method of the getter will return. By default it is State
, but it can be anything that can be constructed from a State const&
.
When the timer thread of the Async<State>
handles the messages and updates its private State
object, it will also update the copies of the State
owned by the Getters - more precisely, the new versions will be sent to the Getter
s using a the lock-free lifo-stack, to avoid data races.
The method update
of the Getter
swaps its local copy of the State
with the last version that the server has sent to it using the the lifo-stack.
The local copy will have been updated by all the messages sent before the last iteration of the timer.
However, the messages sent after the last iteration of the Async<State>
timer, even if they were sent before the call to update
, will not have been handled yet.
Messages are, after all, asynchronous.
In non realtime threads however, you might want to wait for all the messages already sent to the Async<State>
to be handled before update
returns.
In such case, instead of a Getter
, you have to request a BlockingGetter
using the method RequestBlockingGetter
.
The method update
of a BlockingGetter
will wait for the next timer iteration, so that all messages already submitted to the Async<State>
will have been handled before it returns.
An Awaiter
is an object with only one method: await
, which will make the current thread wait for all the already submitted Messages
to be handled by the Async<State>
, by waiting for the completion of the next timer iteration. Each Awaiter
object should only be used by one thread at the same time, as it is for the Getter
s.
The template class LockFreeMessenger<T>
is a wrapper around the Queue World's multiple-producer multiple-consumer LIFO stack. It implements functionality to send and receive data of type T
between threads in a lock-free way, and to preallocate the resources to do so.
The documentation, available at https://unevens.github.io/lockfree-async/, can be generated with Doxygen running