Theron - C++ concurrency library

Frequently asked questions (FAQ)

Can I use Theron in commercial projects?

Yes, you can. There are no licensing fees or royalties to pay. Theron is distributed under the MIT license, since version 3.0. See here for more details.

Does Theron work under Linux?

The latest releases of Theron are tested with Visual Studio and also with gcc via MinGW32 under Windows. Theron has been used on Linux with success; the main thing you need is an installation of Boost, plus you may need to update the paths to Boost near the top of the included makefile. The included makefile builds the library and also the samples, benchmarks and tests -- it should be regarded as a starting point.

Does Theron work under Mac OS?

Yes, Theron has been used in this environment. Again, you may need to update the paths to Boost in the makefile, since they differ from one platform (and maybe, user) to another.

Will Theron work with the new standard C++ threads in C++0x?

Support is planned, and is not especially difficult since std::thread is closely based on boost::thread, which is already supported. If you need this support then give us a shout, otherwise it'll happen sometime soon.

Can I use Theron on (some novel environment)?

Probably. The code is pretty standard/portable and compiles under gcc, which is a good start. The main limitation is the need for an underlying thread library. If Boost is available for the target platform, then use Boost threads via the in-built support. Otherwise, consider wrapping the native thread library in the Theron thread abstraction API -- which consists of only four classes, so is pretty doable.

Does Theron support distributed parallelism across multiple hosts?

Not currently, no. It's something we could look into.

What kinds of things can I send in messages?

When messages are sent they are copied, using the copy-constructors of the message objects. This means that messages must have copy-constructors and assignment operators: they must be copyable. Any class that has a copy-constructor and an assignment operator can be sent as a message, including Plain Old Data types and classes with default compiler-generated implementations.

When writing your own message types, it's important to think about the semantics of message passing. Messages are sent by value, meaning that the recipient receives an independent copy of the same value. It is the job of the class's copy-constructor to implement this copying safely and meaningfully.

Be especially careful when sending non-trivial classes that reference external data via pointers. The copy constructor should ideally deep-copy the referenced data, so that each copy is independent. Doing so ensures thread safety. A good example is std::string, which can safely be used as a message (whereas a simple C string can not).

Sometimes it's desirable to break the rules by sending messages that pass ownership of shared memory. This is possible but should be used with caution since it breaks the semantics of the Actor Model. It becomes the user's responsibility to ensure that no two actors ever reference the shared memory in parallel. One way to do this is to treat the message as a token. Ownership of the token confers on the receiving actor the right to access the shared memory.

As of version 2.07, Theron supports aligned allocation of messages, allowing specialized types that require alignment (typically for use with a hardware feature such as DMA or a vector processor) to be included in messages.

What kind of objects make good actors in Theron?

Although Theron is fairly low-level and C++ in its spirit, you don't want to get carried away and use an actor in every place where you would normally have used an object. Reserve actors for places where they make a difference, and use objects everywhere else.

Since Theron uses a threadpool to execute actor code, it's not like every actor brings with it a whole new thread plus local storage for that thread. Still, each actor has some memory overhead (see below) and some small scheduling cost.

Bear two things in mind: (1) actors execute in parallel, and (2) within each actor, execution is strictly serial. This means it's best to think of actors as the units of concurrency. Using an actor introduces the ability for the code in the actor to execute asynchronously with the code outside; using an object restricts us to synchronous, serial execution. Note also that it's possible for an actor to "contain" other actors, introducing the possibility of parallelism within actors (see below).

Another consideration is that it's often useful to separate out the actor aspects of an object from the underlying/wrapped object aspects. For example, if I have a need to write an actor that acts as a container, I'll write the container as a standard C++ object, then wrap that in an actor. That way I haven't tied the container to Theron or to actors, and can still use it in other projects.

Can Theron actors be nested?

Yes. Actors can contain other actors. This is a neat trick and a powerful mechanism of abstraction, just as it is with objects. And as with objects, we need to be clear about what we mean by "contain".

There are several senses: (1) one object resides in the memory of the other; (2) one object holds a reference (or pointer) to another; (3) one object hides another, via abstraction.

Since actors are allocated by the Framework, actors can't literally be contained within each other, from a memory standpoint. But an actor can hold a reference to another, and can create (and destroy, via garbage collection) other actors. This introduces the possibility of an actor hiding the actors it creates, seeming to the outside world as a single actor, but internally creating and managing any number of other actors that are its internal implementation.

Whereas with conventional objects this kind of abstraction is mainly about storage and ownership, with actors, it also has implications for concurrency. It's fundamental to actors that the code inside each actor (ie. its message handlers) is executed serially, by only one thread at a time. This means that without nesting of actors, each actor effectively prohibits concurrency within the code it contains. Nesting provides the solution: if an actor creates child actors then those child actors execute in parallel, concurrently, reintroducing parallelism into the actor.

To give a concrete example, consider the FileReader actor introduced here. A single FileReader solves the problem of reading files asynchronously, but can still only read one file at a time. If we instead create a pool of n FileReaders, we can read up to n files at once (assuming there are n free worker threads, and ignoring any OS limitations). If we take that further and wrap the pool of FileReaders in a single ParallelFileReader actor which hides the individual FileReaders -- creating them, holding references to them, and dispatching requests to them internally -- then we've created an actor which looks like a single actor to client code, but executes concurrently internally.

What's the memory overhead of actors and messages in Theron?

As of Theron 3.03, actors have a per-actor memory overhead of 64 bytes, in 32-bit builds, and 112 bytes, in 64-bit builds. This overhead is measured as the amount of memory consumed by a trivial, empty actor derived class with no user members at all.

Messages incur an overhead of around 16 bytes per message (in 32-bit builds), but only during sending. The overhead actually applies to the wrapper class in which the message value is wrapped during sending, and is not imposed on the message objects themselves.

Is Theron suitable for use in game development?

This is a complex question with a complicated answer. The short answer is "yes".

As of version 2.06, Theron supports disabling the built-in RTTI (Run-Time Type Information) in C++. This is a requirement of many games systems, where the overhead of RTTI is undesirable.

As of version 2.07, Theron supports aligned allocation of messages and actors, allowing specialized types that require alignment (typically for use with a hardware feature such as DMA or a vector processor) to be included in actors and messages. A common example is 3D math vectors in a game console environment.

Theron allows the user to replace the allocator used to allocate all internal memory within Theron, including messages and actors. This makes it possible to 'ring-fence' Theron within a custom allocator, making its memory usage well-contained. The custom allocator can optionally support aligned allocations -- a requirement if aligned types are used.

Another question is the 'weight' of actors and messages, which is discussed in more detail in a different question. Generally speaking, Theron actors are suitable for medium and large-sized system objects, rather than for tiny objects such as individual vectors. As for messages, objects of pretty much any size can be used, although bear in mind that they are sent by value (ie. copied) and not by reference.

How can I contribute code changes to Theron?

Theron is Open Source in the sense that you have access to the source code and can use it and remix it freely, but not yet in the sense that the source is hosted openly for everyone to make changes. The code is hosted in source control, but not on a public server. You are very welcome to submit changes and suggestions by email.

What are the differences between versions 1.x and 2.x of Theron?

This page has all the details.