A minimal actor framework, part 2
Join the DZone community and get the full member experience.
Join For FreeYesterday I introduced a very minimal actor “framework”, and I noted that while it was very simple, it wasn’t a very good one. The major problems in that implementation are:
- No considerations for errors
- No considerations for async operations
The first one seems obvious, but what about the second one, how can we not consider async operations in an actor framework?
Well, the answer to that is quite simple, our actor framework assumed that we were always going to execute synchronously. That isn’t going to work if there is a need to do things like async IO.
As it happened, that is precisely what I had in mind for this code, so I wrote this:
public class Actor<TState> { public TState State { get; set; } private readonly ConcurrentQueue<Func<TState, Task>> actions = new ConcurrentQueue<Func<TState, Task>>(); private Task activeTask; public void Act(Func<TState, Task> action) { actions.Enqueue(action); lock(this) { if (activeTask != null) return; activeTask = Task.Factory.StartNew(ExecuteActions); } } public event EventHandler<UnhandledExceptionEventArgs> OnError; private void InvokeOnError(UnhandledExceptionEventArgs e) { var handler = OnError; if (handler == null) throw new InvalidOperationException("An error was raised for an actor with no error handling capabilities"); handler(this, e); } private void ExecuteActions() { Func<TState, Task> func; if (actions.TryDequeue(out func)) { func(State) .ContinueWith(x => { if (x.Exception != null) { InvokeOnError(new UnhandledExceptionEventArgs(x.Exception, false)); return; } ExecuteActions(); }); return; } lock(this) { activeTask = null; } } }
Thoughts?
Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments