With Moore's law on life-support, next-generation CPUs are not speeding up as quickly as they used to. To compensate, devices increasingly offer multiple CPUs that share a common block of memory. To leverage this throughput potential, programs need to be re-factored to support concurrency efficiently and safely, avoiding the risk of race conditions and deadlocks.
To make concurrent architectures faster, easier to implement and safer, Cone offers a rich collection of familiar and distinctive mechanisms. Cone's more distinctive mechanisms include its approach to permissions and actors.
Race Conditions and Permissions
To ensure sequential consistency, safeguards are needed to prevent race conditions, where some thread can access objects that incompletely-mutated or invalidated by another thread. Cone has built-in guardrails to automatically prevent race conditions at compile-time.
In addition to familiar mechanisms, such as locks and concurrent data structures, Cone requires the use of permissions on all region-managed and borrowed references. Static permissions make it impossible for two threads to point to the same mutable object at the same time. However, they do allow threads to share access to immutable objects and allow one thread to move a singly-owned mutable object graph from one thread to another.
Cone leans into the actor model as the optimal approach to make concurrency logic efficient, safe and maintainable. Essentially, an actor is simply a thread that owns its own FIFO work queue. Any actor can request work from another actor by passing it new work in the form of a message. This message is appended to the actor's work queue.
Whenever an actor is activated on a CPU, it pops the next piece of work off its queue and performs the work. If a response is expected, the response is sent back as a message to some designated actor.
Cone's implementation of actors is highly efficient due to streamlined communications between actors:
- Messages are often small, typically consisting of a few references to objects that do not need to be moved or copied in memory.
- The ownership of singly-owned mutable objects can be transferred to another actor with needless copying
- Scheduling and dispatch are extremely lightweight, reducing the overhead needed for context-switching
When necessary, it is possible for actors to support shared, mutable access to objects using lock permissions, concurrent data structures, and atomic primitives. Please refer to the reference documentation for more information about actor-based communications.
The actor model also makes possible a versatile design strategy for handling asynchronous I/O.