Obsolete

Note: these api’s are now obsolete and it is recommended instead that you use the Event Group construct to track the asynchronous completion of operations across your actors.

Synchronous execution of actors

Coyote offers the following APIs (and overloads) for synchronous execution of actor creation and event sending.

Task<ActorId> CreateActorAndExecuteAsync(Type type, Event e = null, Guid opGroupId = default);
Task<bool> SendEventAndExecuteAsync(ActorId target, Event e, Guid opGroupId = default, SendOptions options = null);

Both of these are async methods and must be awaited by the caller. The method CreateActorAndExecuteAsync when awaited, returns only when the newly created actor becomes idle. That is, it creates the actor, passes it the initial event e, starts executing the actor, and then waits for the actor to become idle. An actor is idle when no events can be received from its inbox. The method SendEventAndExecuteAsync when awaited has two possible executions. If the target actor is running (i.e., it is not idle) then the method only enqueues the event and returns immediately with the return value false. If the target actor was idle then the method enqueues the event (which causes the target actor to start executing) and waits until the actor becomes idle again. In this case, the method returns true indicating that the event has been processed by the target actor.

Note that this is only one level deep. If the event handler invoked by the actor creation or event handling decides to send more events to other actors, then the above synchronous methods do not wait for that additional work to be completed, unless those events are sent to this.Id which does stop the actor from becoming idle.

Another type of synchronous execution is provided by the Actor method ReceiveEventAsync. This method allows an actor to wait for a given type of event to be received, and can even provide a predicate that conditionally receives the event. This means instead of declaring an event handler like this which means you can receive this event any time and call HandlePoing:

[OnEventDoAction(typeof(PongEvent), nameof(HandlePong))]

You can instead explicitly receive the event in a specific place in your actor like this so that the event is not generally handled at other times:

Event e = await this.ReceiveEventAsync(typeof(PongEvent));
HandlePong(e);

A second overload of ReceiveEventAsync allows you to provide a list of event types each with their own predicates. This version of the method receives the first matching event.

A ReceiveEventAsync call blocks all non-matching events from being dequeued from the actor’s inbox.

Potential deadlocks with ReceiveEventAsync

You should be careful with the use of ReceiveEventAsync when using CreateActorAndExecuteAsync and SendEventAndExecuteAsync. In the absence of ReceiveEventAsync, the semantics of these methods guarantee that the program cannot deadlock. With a ReceiveEventAsync the following situation can occur. Let’s suppose there are two actors A and B and the latter is idle. Then actor A does SendEventAndExecuteAsync to pass an event e to B. Because B was idle, A will wait until B becomes idle again. But if B executes a ReceiveEventAsync while processing the event e, expecting another event from A then the program deadlocks. (Blocking on a ReceiveEventAsync is not considered as being idle.)

Extracting information from an actor

Suppose there is a Coyote actor M1 that holds some information that you are interested in grabbing. The usual way of getting this information would be to SendEvent a “get” message to M1 and then wait for its response via ReceiveEventAsync. However, a ReceiveEventAsync can only be executed by an actor. How do you get the result outside the context of an actor, from, say, a static method? One option is to use these *AndExecuteAsync methods. First define a trampoline actor T that you create from your static method via CreateActorAndExecuteAsync. The trampoline actor, in its OnEntry method of the start state (which is called immediately when a actor is created), sends the “get” message to M1 and waits for its response via ReceiveEventAsync. Once it gets the response, it can stash the result in an object that can be safely shared with the calling static method without any race conditions.

You can use a SharedRegister, which will rule out race conditions as well, but this still requires a separate protocol to know when the result has been made available.

Running an actor synchronously

Another programming pattern is to drive an actor synchronously. The program can do CreateActorAndExecuteAsync to create the actor, then repeatedly do SendEventAndExecuteAsync to make the actor process events one after another. Let’s consider an example. Suppose that we need to define a actor M that is easily decomposed into two smaller actors M1 and M2. For each incoming event, M decides to run one of the two actors; there is no need to run them in parallel. In this case, you only need to code up the smaller actors M1 and M2. The actor M can be a simple wrapper. On instantiation, M creates the two child actors as follows:

ActorId m1 = this.CreateActorAndExecuteAsync(typeof(M1), ...);
ActorId m2 = this.CreateActorAndExecuteAsync(typeof(M2), ...);

When M receives an event e, it will choose to run the appropriate actor as follows:

if (SomeCondition(e))
{
   bool b1 = await this.SendEventAndExecuteAsync(m1, e);
   this.Assert(b1);
}
else
{
   bool b2 = await this.SendEventAndExecuteAsync(m2, e);
   this.Assert(b2);
}

Note that the two assertions above are guaranteed to never fail because the m1 and m2 actors are always left in an idle state by M, provided that M never gives out the ActorId of m1 or m2 to any other actors and does not so long as M1 and M2 do not queue other events on themselves using SendEvent.