Stitch is a C++ library for communication and synchronization of threads and the outside world, suitable for real-time applications.
See the documentation for details.
See the comparison with related software below.
The design of Stitch is guided by the following principles:
Instead of implementing a particular communication model (e.g. Actors, Communicating Sequential Processes, ...), Stitch provides basic building blocks. The blocks are easy to compose, and the user is free to compose them in the way most suitable for their application.
However, Stitch does provide implementations of several common communication patterns built on top of the basic blocks.
Stitch separates two concerns:
- Information: Producing and consuming data; addressed by shared data structures.
- Time: Waiting for something to happen, for example data to be produced and consumed; addressed by the event system.
Despite striving to separate these concerns, Stitch makes sure the classes that represent them are easy to compose. This is demonstrated by Stitch's higher-level classes implementing common communication patterns.
Stitch aims to be suitable for real-time applications. Its goal is to provide at least lock-free progress guarantees and linear or constant time complexity for as many operations as possible. Progress guarantees and complexity should be documented for each individual operation (work in progress).
Stitch provides a number of wait-free and lock-free data structures. Here's an example of a queue and an atom:
struct Data { int x, y, z; }
Stitch::Atom<Data> atom;
Stitch::SPSC_Queue<int> queue;
thread t1([&]()
{
Stitch::AtomWriter atom_writer(atom);
atom_writer.store({ 1, 2, 3 }); // Lock-free
queue.push(123); // Wait-free
});
thread t2([&]()
{
Stitch::AtomReader atom_reader(atom);
Data a = atom_reader.load(); // Lock-free
int b;
bool ok = queue.pop(b); // Wait-free
});
Stitch provides an event system allowing to wait for multiple events of different types.
Stitch::Timer timer;
Stitch::Signal signal;
timer.start(chrono::milliseconds(1000));
thread notifier([&]()
{
this_thread::sleep_for(chrono::milliseconds(500));
signal.notify();
});
Stitch::Event_Reactor reactor;
reactor.subscribe(timer.event(), [&]()
{
cout << "Timer" << endl;
});
reactor.subscribe(signal.event(), [&]()
{
cout << "Signal" << endl;
});
reactor.run(Stitch::Event_Reactor::WaitUntilQuit);
Building upon shared data structures and events, Stitch provides classes that implement common communication patterns.
Stream producers and consumers communicate streams of items. They can be connected in a many-to-many fashion.
struct Work { int input; };
// Create a stream consumer that allows 100 work items to be buffered.
Stitch::Stream_Consumer<Work> consumer(100);
thread consumer_thread([&]()
{
// Consume items until at least 10 are consumed
int count = 0;
while(count < 10)
{
wait(consumer.event())
Work item;
while(consumer.pop(item))
{
++count
cout << item.input << endl;
}
}
});
// Define a producer of work
auto producer_func = [&]()
{
Stitch::Stream_Producer<Work> producer;
connect(producer, consumer);
// Produce 10 work requests, one every 100 ms
for (int v = 0; v < 10; ++v)
{
producer.push({v});
this_thread::sleep_for(chrono::milliseconds(100));
}
};
// Start 2 producers
thread producer1_thread(producer_func);
thread producer2_thread(producer_func);
State updater and observers communicate the latest state of a value.
Stitch::State<int> state;
thread writer_thread([&]()
{
// Update state 10 times, every 100 ms
for (int v = 1; v <= 10; ++v)
{
state.store(v);
this_thread::sleep_for(chrono::milliseconds(100));
}
});
auto observer_func = [&]()
{
Stitch::State_Observer<int> observer;
observer.connect(state);
// React to state changes, until state equals 10
int v;
do {
wait(observer.changed());
v = observer.load();
cout << v << endl;
}
while(v != 10);
};
// Start 2 observers
thread observer1_thread(observer_func);
thread observer2_thread(observer_func);
These are some of our observations about related software which give reasons for the existence of this library. Please contact the authors of this text if any of this is incorrect or if something else should be added.
- Limited to Windows.
- No file events.
- No progress guarantees.
- A higher-level interface (the actor model) instead of basic building blocks.
- Does not specify detailed progress guarantees for each method of each class.
- C instead of C++ (There exist C++ wrappers though).
- Does not provide a direct interface for waiting on events like our
wait(event)
. - Does not provide a direct interface for internally generated events like our
Signal
class.