package actors
import "../uuid"
Anything :: struct {
data: any,
ptr: rawptr,
}
new_anything :: proc(value: $T) -> Anything {
p := new(T)
p^ = value
return Anything{p^, p}
}
free_anything :: proc(any: Anything) {
free(any.ptr)
}
Any :: union {
// booleans
bool,
b8,
b16,
b32,
b64,
// integers
int,
i8,
i16,
i32,
i64,
i128,
uint,
u8,
u16,
u32,
u64,
u128,
uintptr,
// endian specific integers
// little endian
i16le,
i32le,
i64le,
i128le,
u16le,
u32le,
u64le,
u128le,
// big endian
i16be,
i32be,
i64be,
i128be,
u16be,
u32be,
u64be,
u128be,
// floating point numbers
f16,
f32,
f64,
// endian specific floating point numbers
// little endian
f16le,
f32le,
f64le,
// big endian
f16be,
f32be,
f64be,
// complex numbers
complex32,
complex64,
complex128,
// quaternion numbers
quaternion64,
quaternion128,
quaternion256,
// signed 32 bit integer
// represents a Unicode code point
// is a distinct type to `i32`
rune,
// strings
string,
cstring,
// raw pointer type
rawptr,
// runtime type information specific type
typeid,
// custom types
ActorRef,
// containers
[dynamic]Any,
map[string]Any,
}
State :: Any
Behavior :: proc(self: ^Actor, sys: ^System, state: ^State, from: ActorRef, msg: any)
Actor :: struct {
ref: ActorRef,
behavior: Behavior,
last_behavior: Maybe(Behavior),
state: State,
}
ActorRef :: struct {
addr: string,
}
Message :: struct {
to: ActorRef,
from: ActorRef,
msg: Anything,
}
System :: struct {
actors: map[string]Actor,
queue: [dynamic]Message,
running: bool,
}
new_system :: proc() -> ^System {
sys := new(System)
sys.running = true
sys.actors = make(map[string]Actor)
sys.queue = make([dynamic]Message, 0)
return sys
}
destroy_system :: proc(sys: ^System) {
for len(sys.queue) > 0 {
msg := pop(&sys.queue)
free_anything(msg.msg)
}
delete(sys.queue)
delete(sys.actors)
free(sys)
}
spawn :: proc(sys: ^System, state: State, behavior: Behavior) -> ActorRef {
id := uuid.generate()
id_string, err := uuid.clone_to_string(id)
assert(err == nil)
ref := ActorRef{id_string}
sys.actors[id_string] = Actor{ref, behavior, nil, state}
return ref
}
become :: proc(self: ^Actor, behavior: Behavior) {
self^.last_behavior = self^.behavior
self^.behavior = behavior
}
unbecome :: proc(self: ^Actor) {
if self^.last_behavior != nil {
self^.behavior = self^.last_behavior.?
}
}
send :: proc(sys: ^System, from: ActorRef, to: ActorRef, msg: $T) {
m := new_anything(msg)
append(&sys.queue, Message{to, from, m})
}
stop :: proc(sys: ^System) {
sys.running = false
}
work :: proc(sys: ^System) {
for sys.running {
if len(sys.queue) == 0 {
break
}
msg := pop_front(&sys.queue)
actor := &sys.actors[msg.to.addr]
actor.behavior(actor, sys, &actor.state, msg.from, msg.msg.data)
free_anything(msg.msg)
}
}