Reference
Circo.RecipientMovedCirco.ThrowCircoCore.ActorCircoCore.ActorIdCircoCore.AddrCircoCore.CircoContextCircoCore.InfotonCircoCore.NameQueryCircoCore.PosCircoCore.PostCodeCirco.migrateCircoCore.addrCircoCore.apply_infotonCircoCore.becomeCircoCore.boxCircoCore.boxCircoCore.dieCircoCore.getnameCircoCore.isbaseaddressCircoCore.isnulladdrCircoCore.onmessageCircoCore.onmigrateCircoCore.onspawnCircoCore.posCircoCore.redirectCircoCore.registernameCircoCore.sendCircoCore.spawn
Items from CircoCore are reexported thus available directly from Circo.
CircoCore.Actor — Typeabstract type Actor{TCoreState}Supertype of all actors.
Subtypes must be mutable and must provide a field core::TCoreState that can remain undefined after creation.
Examples
mutable struct DataHolder{TValue, TCore} <: Actor{TCore}
value::TValue
core::TCore
endCircoCore.ActorId — TypeActorIdA cluster-unique id that is randomly generated when the actor is spawned (first scheduled).
ActorId is an alias to UInt64 at the time, so it may pop up in error messages as such.
CircoCore.Addr — TypeAddr(postcode::PostCode, box::ActorId)
Addr(readable_address::String)
Addr()The full address of an actor.
When created without arguments, it will be the null address. See isnulladdr()
If the referenced actor migrates to a different scheduler, messages sent to the old address will bounce back as RecipientMoved and the Addr must be updated manually.
Examples
Addr("192.168.1.11:24721", 0xbc6ac81fc7e4ea2)
Addr("192.168.1.11:24721/bc6ac81fc7e4ea2")
CircoCore.CircoContext — TypeCircoContext(;options...) <: AbstractContextStore configuration, manage staging and run-time code optimizations for Circo.
CircoCore.Infoton — TypeInfoton(sourcepos::Pos, energy::Real = 1)Create an Infoton that carries abs(energy) amount of energy and has the sign sign(energy).
The infoton mediates the force that awakens between communicating actors. When arriving at its target actor, the infoton pulls/pushes the actor toward/away from its source, depending on its sign (positive pulls).
The exact details of how the Infoton should act at its target is actively researched. Please check or overload apply_infoton.
CircoCore.NameQuery — TypeNameQuery(name::String) <: RequestA query that can be sent to a remote scheduler for querying its local registry.
CircoCore.Pos — TypePos(x::Real, y::Real, z::Real)
Pos(coords)A point in the 3D "actor space".
You can access the coords by pos.x, pos.y, pos.z.
Pos is implemented using an SVector{3, Float32}.
CircoCore.PostCode — TypePostCodeA string that identifies a scheduler.
Examples
"192.168.1.11:24721"
CircoCore.addr — Methodaddr(a::Actor)Return the address of the actor.
Call this on a spawned actor to get its address. Throws UndefRefError if the actor is not spawned.
CircoCore.apply_infoton — Methodapply_infoton(targetactor::Actor, infoton::Infoton)An infoton acting on an actor.
Please check the source and the examples for more info.
CircoCore.become — Methodbecome(service, old::Actor, reincarnated::Actor)Reincarnates the old actor into new, meaning that old will die, and reincarnated will be spawned reusing the address of old.
Note: As the name suggests, become is the Circonian way of behavior change.
CircoCore.box — Methodbox(a::Actor)Return the 'P.O. box' of the spawned actor.
Call this on a spawned actor to get its id (aka box). Throws UndefRefError if the actor is not spawned.
CircoCore.box — Methodbox(a::Addr)::ActorIdReturn the box of the address, that is the id of the actor.
When the actor migrates, its box remains the same, only the PostCode of the address changes.
CircoCore.die — Methoddie(service, me::Actor)Unschedule the actor from its current scheduler.
CircoCore.getname — Methodfunction getname(service, name::String)::Union{Addr, Nothing}Return the registered name from the scheduler-local registry, or nothing.
See also: NameQuery
CircoCore.isbaseaddress — Methodisbaseaddress(addr::Addr)::BoolReturn true if addr is a base address, meaning it references a scheduler directly.
CircoCore.isnulladdr — Methodisnulladdr(a::Addr)Check if the given address is a null address, meaning that it points to "nowhere", messages sent to it will be dropped.
CircoCore.onmessage — Methodonmessage(me::Actor, message, service)Handle a message arriving at an actor.
Only the payload of the message is delivered, there is currently no way to access the infoton or the sender address. If you need a reply, include the sender address in the request.
Note: Do not forget to import it or use its qualified name to allow overloading!
Examples
import CircoCore.onmessage
struct TestRequest
replyto::Addr
end
struct TestResponse end
function onmessage(me::MyActor, message::TestRequest, service)
send(service, me, message.replyto, TestResponse())
endCircoCore.onmigrate — Methodonmigrate(me::Actor, service)Lifecycle callback that marks a successful migration.
It is called on the target scheduler, before any messages will be delivered.
Note: Do not forget to import it or use its qualified name to allow overloading!
Examples
import CircoCore.onmigrate
function onmigrate(me::MyActor, service)
@info "Successfully migrated, registering a name on the new scheduler"
registername(service, "MyActor", me)
endCircoCore.onspawn — MethodCircoCore.onspawn(me::Actor, service)Lifecycle callback that marks the first scheduling of the actor, called during spawning, before any onmessage.
Note: Do not forget to import it or use its qualified name to allow overloading!
Examples
import CircoCore.onspawn
funtion onspawn(me::MyActor, service)
registername(service, "MyActor", me) # Register this actor in the local name service
endCircoCore.pos — Methodpos(a::Actor)::Posreturn the current position of the actor.
Call this on a spawned actor to get its position. Throws UndefRefError if the actor is not spawned.
CircoCore.redirect — Methodredirect(addr::Addr, topostcode::PostCode):AddrCreate a new Addr by replacing the postcode of the given one.
CircoCore.registername — Methodregistername(service, name::String, actor::Actor)Register the given actor under the given name in the scheduler-local name registry.
Note that there is no need to unregister the name when migrating or dying
TODO implement manual and auto-unregistration
CircoCore.send — Methodsend(service, sender::Actor, to::Addr, messagebody::Any, energy::Real = 1; timeout::Real = 2.0)Send a message from an actor to an another.
Part of the actor API, can be called from a lifecycle callback, providing the service you got.
messagebody can be of any type, but a current limitation of inter-node communication is that the serialized form of messagebody must fit in an IPv4 UDP packet with ~100 bytes margin. The exact value depends on the MTU size of the network and changing implementation details, but 1380 bytes can be considered safe. You may be able to tune your system to get higher values.
If messagebody is a Request, a timeout will be set for the token of it. The timeout keyword argument can be used to control the deadline (seconds).
energy sets the energy and sign of the Infoton attached to the message.
Examples
const QUERY = "The Ultimate Question of Life, The Universe, and Everything."
mutable struct MyActor <: Actor{TCoreState}
searcher::Addr
core::CoreState
MyActor() = new()
end
struct Start end
struct Search
query::String
end
[...] # Spawn the searcher or receive its address
function CircoCore.onmessage(me::MyActor, message::Start, service)
send(service,
me,
me.searcher,
Search(QUERY, addr(me)))
endImplementation
Please note that service is always the last argument of lifecycle callbacks like onmessage. It's because onmessage is dynamically dispatched, and service provides no information about where to dispatch. (Only one service instance exists as of v"0.2.0") Listing it at the end improves the performance.
On the other hand, actor API endpoints like send are always statically dispatched, thus they can accept the service as their first argument, allowing the user to treat e.g. "spawn(service" as a single unit of thought and not forget to write out the meaningless service.
Consistency is just as important as convenience. But performance is king.
CircoCore.spawn — Methodspawn(service, actor::Actor, [pos::Pos])::AddrSpawn the given actor on the scheduler represented by service, return the address of it.
Part of the actor API, can be called from a lifecycle callback, providing the service you got.
The onspawn callback of actor will run before this function returns.
Examples
TODO: update this sample
mutable struct ListItem{TData, TCore} <: Actor{TCore}
data::TData
next::Union{Nothing, Addr}
core::TCore
ListItem(data, core) = new{typeof(data), typeof(core)}(data, nothing, core)
end
struct Append{TData}
value::TData
end
function CircoCore.onmessage(me::ListItem, message::Append, service)
me.next = spawn(service, ListItem(message.value))
endCirco.RecipientMoved — TypeRecipientMoved{TBody}If a message is not delivered because the target actor moved to a known location, this message will be sent back to the sender.
The original message gets included in the RecipientMoved message. Resending to the new location and updating the address in their storage is the responsibility of the original sender.
struct RecipientMoved{TBody}
oldaddress::Addr
newaddress::Addr
originalmessage::TBody
endCirco.Throw — TypeThrowMessage that throws an error from the monitoring actor
Circo.migrate — Methodmigrate(service, actor::Actor, topostcode::PostCode)