Skip to content

neurocyte/thespian

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

134 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thespian

Fast & flexible actors for Zig, C & C++ applications

To build:

zig build

See tests/* for many interesting examples.

To run the tests:

zig build test

Architecture Overview

Thespian is an actor framework where concurrent units ("instances") communicate exclusively via message passing, with no shared mutable state. Messages are encoded using CBOR (Concise Binary Object Representation).

The library has three layers:

  1. C++ core - the primary implementation (src/instance.cpp, src/executor_asio.cpp, src/hub.cpp)
  2. C API - a thin wrapper layer in src/c/ exposing the C++ core to other languages
  3. Zig module - src/thespian.zig wraps the C API into idiomatic Zig types with comptime features

Key Concepts

context - the runtime environment. You create one, spawn actors into it, then call run(). It owns the executor thread pool.

instance / actor - each spawned actor is a behaviour (a std::function<result()>) that registers a receiver to handle incoming messages. Actors run on an executor. Currently the only executor provided in this repo is one backed by ASIO.

handle / pid - a reference to a live actor. In C++ this is thespian::handle; in Zig it is pid (owned) or pid_ref (borrowed). Sending to an expired handle is safe and fails gracefully.

message - a CBOR-encoded byte buffer. Pattern matching uses extract/match with a composable matcher DSL (string, array, any, more, etc.).

hub - a pub/sub broadcast actor. Subscribers receive all broadcasts; supports filtering and Unix socket-based IPC.


I/O Primitives

Primitive Purpose
timeout One-shot timer
metronome Repeating timer
signal OS signal handling
tcp_acceptor / tcp_connector TCP networking
unx_acceptor / unx_connector Unix domain sockets
socket Raw FD-backed socket I/O
file_descriptor Async wait on arbitrary file descriptors (posix)
file_stream Async read/write on file handles (win32)
subprocess Child process management (platform-specific)

⚠️ I/O Primitive Ownership - Critical Rule

Every I/O primitive is owned by the actor that was executing at the time it was created. Async completion messages from a primitive are delivered only to that owning actor. There is no way to transfer ownership or redirect delivery to a different actor after construction.

A common mistake is to create an I/O primitive in one actor and then spawn a separate "handler" actor, passing the primitive (or its handle) to it. The handler will never receive any events - they will continue to be delivered to the original actor that created the primitive.

The correct pattern: the actor responsible for handling a primitive's events must be the one that creates it. If you need a dedicated handler actor, have it create its own primitives from within its own behaviour.


Ownership & Lifecycle

  • Zig's pid is Owned (must call deinit()); pid_ref is Wrapped (borrowed, no deinit required)
  • Actors can trap() exit signals and link() to other actors for supervision
  • spawn_link ties an actor's lifetime to its parent

Build System

  • Zig 0.15.2+ required
  • Dependencies: cbor (CBOR codec), asio (ASIO network executor), tracy (optional profiling via -Denable_tracy=true)
  • Produces a static library libthespian and a Zig module
  • Tests live in test/ and cover both the C++ and Zig APIs

Misc

See DeepWiki for more documentation: Ask DeepWiki

About

thespian: an actor library for Zig, C & C++ applications

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors