private static void AgentExample() { //agents provide thread safety to individual functions. //in this case, we want one point for storing to the database that can be shared using var dataAccess = new AsyncAgent <object>(StoreToDatabase, x => { }); object latestData = new object(); //this could be called from multiple places, but all saves are sequential and thread safe dataAccess.Publish(latestData); Console.ReadKey(); }
public ReactiveAsyncAgent( TState initialState, Func <TState, TMessage, CancellationToken, Task <TState> > messageHandler, Func <Exception, CancellationToken, Task <bool> > errorHandler) { if (initialState == null) { throw new ArgumentNullException(nameof(initialState)); } if (messageHandler == null) { throw new ArgumentNullException(nameof(messageHandler)); } if (errorHandler == null) { throw new ArgumentNullException(nameof(errorHandler)); } _stateSubject = new BehaviorSubject <TState>(initialState); _asyncAgent = new AsyncAgent <TState, TMessage>( initialState: initialState, messageHandler: async(state, msg, ct) => { TState newState = state; newState = await messageHandler(state, msg, ct); if (!ct.IsCancellationRequested) { _stateSubject.OnNext(newState); } return(newState); }, errorHandler: async(ex, ct) => { bool shouldContinue = false; shouldContinue = await errorHandler(ex, ct); if (!shouldContinue && !ct.IsCancellationRequested) { _stateSubject.OnError(ex); } return(shouldContinue); }); State = _stateSubject.AsObservable(); }
public async Task AgentCanHandleMessage() { var tcs = new TaskCompletionSource <string>(); var message = "test"; var agent = new AsyncAgent <string, string>( string.Empty, async(state, msg, ct) => { await Task.Delay(0, ct); tcs.SetResult(msg); return(state); }, (ex, ct) => Task.FromResult(true)); agent.Send(message); var processedMessage = await tcs.Task; Assert.Equal(message, processedMessage); }
public async Task AgentDoesNotHandleMessagesAfterDispose() { var tcs = new TaskCompletionSource <int>(); var agent = new AsyncAgent <int, int>( initialState: 0, messageHandler: async(state, msg, ct) => { await Task.Delay(0, ct); tcs.SetResult(msg); return(state); }, errorHandler: (ex, ct) => Task.FromResult(true)); agent.Dispose(); agent.Send(1); await Task.Delay(50); Assert.False(tcs.Task.IsCompleted); }
public async Task AgentHandlesMessagesInOrder() { var parallelHandlers = 0; var random = new Random(); Exception thrownException = null; var range = Enumerable.Range(0, 10); var tasks = range.Select(_ => new TaskCompletionSource <int>()).ToList(); var agent = new AsyncAgent <int, int>( initialState: 0, messageHandler: async(state, msg, ct) => { if (1 != Interlocked.Increment(ref parallelHandlers)) { throw new Exception("parallelHandlers should be 1"); } await Task.Delay(random.Next(5)); if (0 != Interlocked.Decrement(ref parallelHandlers)) { throw new Exception("parrallelHandlers should be 0"); } tasks[msg].SetResult(msg); return(state); }, errorHandler: (ex, ct) => { thrownException = ex; return(Task.FromResult(true)); }); foreach (var msg in range) { agent.Send(msg); } await Task.WhenAll(tasks.Select(item => item.Task).ToArray()); Assert.Null(thrownException); }
public async Task AgentTriggersErrorHandler() { var tcs = new TaskCompletionSource <Exception>(); var message = "test"; var exception = new Exception(); var agent = new AsyncAgent <string, string>( initialState: string.Empty, messageHandler: async(state, msg, ct) => { await Task.Delay(0, ct); throw exception; }, errorHandler: (ex, ct) => { tcs.SetResult(ex); return(Task.FromResult(true)); }); agent.Send(message); var triggeredException = await tcs.Task; Assert.Equal(exception, triggeredException); }