Reputation: 23214
async await support within actors
I'm porting the actor lib Akka to .NET (https://github.com/rogeralsing/Pigeon) I want to add async/await support inside my actors.
This gives me some problems right now since if I use the default scheduler, the await continuation will run w/o respect to the actors concurrency boundarys. Meaning, the continuation may run when the actor is processing a message since that would lead to two threads accessing the actors internal state at the same time.
If I could somehow schedule the the tasks to the actors own mailbox, and complete the task in the mailbox run, this would solve the problem.
public class SomeActor : UntypedActor
{
protected override OnReceive(object message)
{
if (message is SomeMessage)
{
DoAsyncStuff();
}
}
private async void DoAsyncStuff()
{
var res = await something..
//this code will not respect the actor concurrency boundary
//since it can run at the same time as OnReceive
Console.WriteLine(res);
}
}
I do have a thread static context for the actor, so when the actor is executing, this context is set. So I could easily lookup the active actors mailbox from a task scheduler. Something along the lines of:
public class ActorTaskScheduler : TaskScheduler
{
protected override void QueueTask(Task task)
{
var self = ActorCell.Current.Self;
self.Tell(new ActorTask(task), ActorRef.NoSender);
}
And the ActorTask message could be handled by the system message handler of the actor. So far so good.
I just don't know what to do next. Can I just over write the current TaskScheduler? Is that thread static? I just want to apply this scheduler when an actor is running, it may not affect code running outside the actors.
Is this possible at all to apply custom task schedulers for specific actions only?
Upvotes: 3
Views: 1483
Reputation: 457207
Can I just over write the current TaskScheduler?
The proper way to "set" the current scheduler is to queue a delegate to the scheduler you want. When the delegate executes, it will have that scheduler as "current".
I do have a thread static context for the actor, so when the actor is executing, this context is set.
Is it possible to do this another way? Because then you have an easier solution available.
I'm thinking you could just run each actor inside a ConcurrentExclusiveSchedulerPair.ExclusiveScheduler
. This would mean any actor code runs on a thread pool thread; it could be any thread pool thread, but the ExclusiveScheduler
will ensure only one part of actor code would run at a time.
This "lifts" the actor abstraction from threads to tasks, which is an approach I would recommend if you could do it. It relieves pressure on memory and the thread pool when you have async actors. Of course, things like thread statics can't be used then, so you have to use something like logical call context or a Current
value that is set by a custom SynchronizationContext
or TaskScheduler
.
Upvotes: 9