Laughably simple yet effective Actor concurrency framework for C++20
Concurrency is a breeze. Also a nightmare, if you ever used synchronization techniques. Mostly a nightmare, though. This tiny library allows you to forego sync primitives in favor of object-oriented concurrent actors, executing independently from each other in parallel and receiving messages to react to - one at a time each.
Take a listen.
std::thread
objects.MaybeLater<A>
.Here is a short taste.
#include <iostream>
#include <Laugh/Actor.inl>
using namespace laugh;
struct Counter: Actor
{
int i = 0;
int Increment() { return ++i; }
};
struct User: Actor
{
void PlayAround()
{
auto counter = Self().Make<Counter>();
for(int i = 0; i < 50; ++i)
{
// Fifty concurrent messages!
counter.Bang(&Counter::Increment);
}
// How far has the counter come so far?
Ask(counter, &Counter::Increment)
->AndThen([](ActorRef<Actor> self, int there)
{
std::cout << "So far, he's at " << there
<< std::endl;
});
}
};
int main()
{
// Let a set of three OS threads do the message processing.
ActorContext acts{3};
// Just create an actor like this. Even with arguments,
// if you want to. And if the constructor allows it.
auto user = acts.Make<User>();
// Done and done.
user.Bang(&User::PlayAround);
std::cout << "Meanwhile, main thread sips coffee." << std::endl;
}
For more information on actors as concurrency units, you'll find plenty on the internet. A good place to get to know actors is Akka, which this project is trying to ape a bit (and fail horribly at it).
You'll need a fairly compliant C++20 compiler and CMake. For generating the documentation, Doxygen will be necessary.
I have tested this on Clang 13.0.0 and g++-11 on an Apple macOS machine, but I'm confident that this project will compile without any hassle on the vc++ compiler as well, since this project only depends on the standard library and the concurrentqueue module, which is itself C++11 compliant.
Note that Apple Clang 13.0.0 will not compile this as of October 14, 2021, and neither will all Clang compilers of version 12 and lower; on a Macintosh I recommend getting a fresh Clang 13.0.0 build and doing:
# If you're using homebrew, great! Easy-peasy.
brew install llvm doxygen
To build the project and examples, do
# Clone the repo.
git clone https://github.com/tyckesak/laugh
cd laugh
# If you don't have a recent enough C++20 compiler in your $PATH, append
# the CMake flag '-DCMAKE_CXX_COMPILER=«Path to compiler»'
cmake .
# Then do
cmake --build .
For installing the library and the dependency headers, do
cmake --install .
To build the docs, do
cmake --build . --target docs
To re-download the concurrentqueue module, do
# Might need to do this if the compiler can't find the
# concurrentqueue headers.
cmake --build . --target concurrentqueue
The examples and documentation will tell you the rest.
Whenever a foreign library owns your memory, it might be a good idea to
ask how much memory it occupies and in what way. I am planning for allocator
support some time in the future; in the meantime, I have taken to finding out
how much memory each of the critical pieces of infrastructure use. The numbers
below have been obtained by compiling the project and examples under a Macintosh
x64 using the g++
11 compiler - the results might change depending on the compiler,
architecture and OS used to compile it on.
Actor
's minimum footprint is 32 bytes plus x, depending on how many
members your actor subclasses add.ActorRef<A>
takes up 16 bytes of space.Actor
s.EventualResponse<A>
.I try to keep a clean online repo with as few files as possible to make exploration easier - upon building the project on your local machine however, you'll see a couple more files and directories spring into existence inside the project root directory, including - but not limited to
Extern/
- external project dependencies are cloned there, i.e. concurrentqueue
Library/
- the directory containing the shared/static library after buildingDoxygen/
- contains the Doxygen generated doc filesCMakeFiles/
- intermediate object filesI thought I would do a quick sketch of what a code-light C++ actor framework would look like, and it started to become something quite usable. The contraction of "Light Actor Framework" would be LAF, spoken quickly it eventually turned into "laugh", which turned out to be fitting because this project is laughably small, and easy to grasp and use.
@cameron314 and contributors on making a concurrent queue that really stands out!