The geocentric model of the universe is intuitive and perfectly aligned with common sense. After all, we see the sun, planets, and stars circling an unmoving Earth, don’t we? The model is convoluted, but it “works” well enough—for casting horoscopes (historically the main practical application).
The heliocentric model, by contrast, is counterintuitive and violates “common sense”. It demotes Earth from the center of everything to just another planet. (An idea completely ridiculous at the time: if the Earth was moving, wouldn’t the clouds and birds be left behind?) Yet it is far simpler and explains the seasons, retrograde motion, and planetary positions with elegant clarity. Most importantly, it predicts reality better.
These two worldviews coexisted in tension for centuries.
A remarkably similar tension exists today between the RTOS Model and the Actor Model of computation.
The RTOS Model: Intuitive but Intrinsically Complex
The RTOS worldview—rooted in shared‑state concurrency—feels natural. Control resides in the central Application consisting of RTOS tasks. When a task needs something, it voluntarily blocks in-line on the RTOS (semaphores, delays, event flags, etc.). When tasks share a resource, they protect it with a mutex.
It’s all very intuitive and aligns with common sense.
But the model is only superficially easy. Most developers underestimate the intrinsic complexities and the expertise required to use the overabundance of blocking primitives correctly and safely under all circumstances, including corner cases. Despite this, the RTOS model has worked well enough for decades, much like the geocentric model worked for early astronomers.
The Actor Model: Counterintuitive but Fundamentally Simpler
The Actor Model—built on event‑driven execution and the shared‑nothing principle—initially feels backwards. Control is inverted: the Actor framework is in the center, and it drives execution, while the Application merely reacts. Many newcomers find this unsettling and counterintuitive.
Yet the model is fundamentally simple.
All interactions are asynchronous event exchanges. Resources are encapsulated inside Actors, which serialize access and eliminate the need for mutexes or shared‑state coordination. Concurrency hazards disappear by construction.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Why the Analogy Matters
For centuries, Copernicus’ model was dismissed as “unproven” and “heretical until corrected.” Critics argued that motion is relative, so why bother changing the reference point? But the shift in worldview unlocked Kepler, Galileo, and Newton. Without abandoning the intuitive model, the deeper truths would have remained inaccessible.
The RTOS vs. Actor debate is at a similar inflection point.
The question is no longer which model “works”—they both do—but which one is closer to reality. Embedded systems are inherently event‑driven. The model that reflects this directly is the more accurate and ultimately the more powerful.
Coroutines vs. State Machines
The asynchronous event processing and run‑to‑completion semantics of the Actor model creates a unique problem (not present in the traditional blocking RTOS model): How to represent the internal control logic of each Active Object between events (so that the next event can pick up where the previous event left off)? That choice inevitably leads to the question: coroutines or state machines?
Many developers describe state machines as “cumbersome” and prefer to stay with the familiar, sequential, blocking style of programming where the code appears to remain in control at all times. Coroutines are one such attempt to preserve this mental model. Other variants include protothreads, async/await in Rust, and similar mechanisms across modern languages.
This resistance to inversion of control is understandable. It mirrors the historical reluctance to abandon the “most natural” idea of an unmoving Earth at the center of the universe. The geocentric model felt right because it matched everyday intuition.
Before the heliocentric model was fully accepted, several intermediate geo‑heliocentric models emerged. One of the most famous was the Tychonic system, where the Earth remained fixed at the center, the Sun orbited the Earth, and all other planets orbited the Sun.
This analogy extends cleanly to the tension between coroutines and state machines. Coroutines represent these intermediate models: they preserve the appearance of sequential, blocking control flow while internally relying on compiler‑generated state machines to make suspension and resumption possible.
But this is the key point: coroutines are state machines — just implicit ones. The compiler hard‑codes yield points into a fixed control structure, inheriting many of the inflexibilities of truly blocking threads. The resulting state machines are necessarily limited: they cannot express state hierarchy, cannot factor shared behavior cleanly, and cannot leverage the full expressive power of explicit Hierarchical State Machines.
Coroutines also trade one set of complexities for another. Instead of states and transitions, developers must now reason about promises, yields, resumes, wakers, futures, and executors. Rust’s async/await, for example, requires understanding how futures are polled, how wakers propagate readiness, and how executors drive progress. The vocabulary changes, but the underlying conceptual load remains.
In the end, the same question returns: Which model is closer to reality? Embedded systems are inherently event‑driven. The model that reflects this directly — explicit, analyzable, hierarchical state machines — is the one that ultimately aligns with the true structure of the problem, just as the heliocentric model aligned with the true structure of the solar system.
About the Actor Model
The Actor Model of computation was first introduced by Carl Hewitt in the early 1970s as a formal way to describe concurrent, distributed systems based on autonomous entities communicating through asynchronous messages. Unfortunately, the term Actor was later reused by Ivar Jacobson in the context of use cases, which then became part of UML. To avoid confusion, UML adopted the term “Active Object” for its concurrency construct—conceptually related, but not identical, to Hewitt’s original Actors.
The Actor Model has been highly influential in general‑purpose programming. It inspired full‑fledged Actor‑based languages such as Erlang and Scala, as well as widely used frameworks like Akka. In the real‑time and embedded domain, Actor‑like ideas appeared in methodologies such as ROOM (Real‑Time Object‑Oriented Modeling) and research projects like Ptolemy at UC Berkeley, both of which emphasized event‑driven, message‑based concurrency.
As for practical, production‑grade Actor frameworks for C and C++ on microcontrollers, the most mature and widely adopted are the QP Real‑Time Event Frameworks from Quantum Leaps, which bring Active Objects and Hierarchical State Machines to deeply embedded systems with deterministic, real‑time behavior.