Building an actor framework in Spinel
Disclaimer: This is going to be an ongoing post, I will keep it updated as I do more development.
The threads in Spinel are parallel without GVL, and they are basically lightweight with large number of M threads multiplexed into fewer N OS workers (OS threads), probably just like Erlang processes. Therefore, it should be possible to build an actor framework using those threads.
I have started working on an actor framework many years back, I knew the basics but never had an opportunity to use actor frameworks in production. My exposure to concurrency was very limited back then. After joining TrueCaller and working on an in house ad server pushed me to learn more about Akka framework and explore it in details. Before Akka I spent some time on Elixir and tried to understand more about actors, supervisors and the mechanism. But working on ad server at scale gave me more insights.
On last weekend after reading about the spinel threads I restarted my effort in creating a simple very limited actor library with my fresh perspective.
While working on it, I found a new bug in the Spinel compiler's code-gen. I tried to simulate an expensive computation as follows.
# File fib.rb
def fib(n)
a, b = 0, 1
n.times do
c = a + b
a = b
b = c
end
a
end
p fib(300000)
In Ruby we can run this program it works perfectly, ruby interpreter promotes integer to Bignum when required. But when compiling using spinel we need to tell the compiler that we need to promote the number to Bignum when needed. Otherwise, integer overflow occurs and program crashed while running.
integer overflow in + (RangeError)
So we need to instruct the compiler using a compiler flag --int-overflow=promote.
$ ./spinel --int-overflow=promote fib.rb
But spinel throws an error saying.
fib.rb:2: error: incompatible integer to pointer conversion assigning to 'sp_Bigint *' (aka 'struct sp_Bigint *') from 'sp_int' (aka 'long') [-Wint-conversion]
fib.rb:2: error: incompatible integer to pointer conversion assigning to 'sp_Bigint *' (aka 'struct sp_Bigint *') from 'sp_int' (aka 'long') [-Wint-conversion]
fib.rb:2: error: incompatible integer to pointer conversion assigning to 'sp_Bigint *' (aka 'struct sp_Bigint *') from 'sp_int' (aka 'long') [-Wint-conversion]
spinel: C compilation failed
spinel: the generated C is kept at /var/folders/p7/r09j45l150xfdf1zmyddlfrm0000gp/T//spinel_out_15725_0.c
The error happened because of the multiple assignment in the single line a, b = 0, 1. When I changed it into single assignment in separate lines the error goes and programs works as expected.
Interestingly the error wasn't there on the first spinel release. I have verified this separately.
$../spinel/spinel-2026.09.12/spinel --int-overflow=promote programs/fib.rb
The program worked as expected with the old release. But in the latest main the issue was still present. Therefore, I have raised an issue on Github, and suggested a fix. On the next morning to my surprise Matz immediately made a fix and closed the issue referencing my issue in his commit. To be honest I never expected such a fast response form him, or may be this is done by his Claude agent.
Anyway the issue didn't affect my development. I have created a simple POC version of actor framework here.
As I progressed I spotted another bug in Enumerable#cycle which I reported here. Matz merged a fix and the issue go resolved. His agent is super cool btw.
The actor model
The fundamental idea comes from 1973 paper 'A Universal Modular ACTOR Formalism for Artificial Intelligence' by Carl Hewitt, Peter Bishop, and Richard Steiger. Later by another important paper 'Actor Model of Computation: Scalable Robust Information Systems'. The model is well explained by the author Hewitt in this youtube video.
Fundamentally actors are independent computational units living within a system called 'Actor system'. These units wait for a message and act on it. The outer world interact with actors within the 'actor system' via sending a message to it.
So in simple word, the actor can be thought of as a process with a mailbox. The process wait for a message in the mailbox, as soon as the message arrives in the mailbox it act on the message. The actor logic decides what need to be done with the results. An actor may send the result to another actor via message passing, or even discard the result. There can be any number of actors within the system, they are concurrent and share nothing. Therefore the system can actually execute actors in parallel.
Actor system does the book keeping, it registers all the actors available and help in communicating between the actors via actor address.
Actor address
An actor in the actor system is identified by its address. Actor have the following address format.
actor_id@actor_system_id.hostname
The address is unique and this allows actors to communicate even with actors on a different machine.
Supervisor
A supervisor is a special actor that manages a group of actors. We generally need more than one actor to perform a set of similar tasks, each task can be executed concurrently. A supervisor acts a load balancer to multiple actors performing a particular task. In this scenario we commuicate with supervisor instead of communicating directly with the worker actors. Supervisor forward the message to the next available actor in a round robin fashion.
To be continueed