From 16cca6fa15fb0c13e0fee4d9665e7c9adc4c2c3c Mon Sep 17 00:00:00 2001 From: Rutherther Date: Fri, 20 Jan 2023 11:00:06 +0100 Subject: [PATCH] feat(core): add support for timeout moving to errors --- .../Contracts/ContractBuilder.cs | 19 ++++++-- .../Contracts/DefaultContract.cs | 47 ++++++++++++++++--- Core/NosSmooth.Core/Contracts/IContract.cs | 7 +++ 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/Core/NosSmooth.Core/Contracts/ContractBuilder.cs b/Core/NosSmooth.Core/Contracts/ContractBuilder.cs index a964c49..00711c1 100644 --- a/Core/NosSmooth.Core/Contracts/ContractBuilder.cs +++ b/Core/NosSmooth.Core/Contracts/ContractBuilder.cs @@ -29,7 +29,7 @@ public class ContractBuilder private readonly TState _defaultState; private readonly Dictionary.StateActionAsync> _actions; - private readonly Dictionary _timeouts; + private readonly Dictionary _timeouts; private TState? _fillAtState; private DefaultContract.FillDataAsync? _fillData; @@ -44,7 +44,7 @@ public class ContractBuilder _contractor = contractor; _defaultState = defaultState; _actions = new Dictionary.StateActionAsync>(); - _timeouts = new Dictionary(); + _timeouts = new Dictionary(); } /// @@ -56,7 +56,20 @@ public class ContractBuilder /// The updated builder. public ContractBuilder SetTimeout(TState state, TimeSpan timeout, TState nextState) { - _timeouts[state] = (timeout, nextState); + _timeouts[state] = (timeout, nextState, null); + return this; + } + + /// + /// Sets timeout of the given state. + /// + /// The state to set timeout for. + /// The timeout span. + /// The error to set. + /// The updated builder. + public ContractBuilder SetTimeout(TState state, TimeSpan timeout, TError error) + { + _timeouts[state] = (timeout, null, error); return this; } diff --git a/Core/NosSmooth.Core/Contracts/DefaultContract.cs b/Core/NosSmooth.Core/Contracts/DefaultContract.cs index c80b91c..59da9df 100644 --- a/Core/NosSmooth.Core/Contracts/DefaultContract.cs +++ b/Core/NosSmooth.Core/Contracts/DefaultContract.cs @@ -44,7 +44,7 @@ public class DefaultContract : IContract private readonly SemaphoreSlim _semaphore; - private readonly IDictionary _timeouts; + private readonly IDictionary _timeouts; private readonly IDictionary _actions; private readonly Contractor _contractor; private readonly TState _defaultState; @@ -75,7 +75,7 @@ public class DefaultContract : IContract TState fillAtState, FillDataAsync fillData, IDictionary actions, - IDictionary timeouts + IDictionary timeouts ) { _semaphore = new SemaphoreSlim(1, 1); @@ -139,8 +139,16 @@ public class DefaultContract : IContract if (resultData.Error is not null) { _error = resultData.Error; - _waitCancellationSource?.Cancel(); - return ContractUpdateResponse.Interested; + try + { + _waitCancellationSource?.Cancel(); + } + catch + { + // ignored + } + + return ContractUpdateResponse.InterestedAndUnregister; } if (resultData.NextState is null) @@ -170,6 +178,7 @@ public class DefaultContract : IContract { _error = error; _waitCancellationSource?.Cancel(); + return new ContractError(error.Value); } if (state is not null) @@ -199,6 +208,16 @@ public class DefaultContract : IContract ); } + if (_error is not null) + { + return new ContractError(_error.Value); + } + + if (_resultError is not null) + { + return Result.FromError(_resultError.Value); + } + if (CurrentState.CompareTo(state) >= 0) { // already reached. return Data; @@ -234,6 +253,8 @@ public class DefaultContract : IContract { Unregister(); } + + _waitCancellationSource?.Dispose(); } if (ct.IsCancellationRequested) @@ -259,6 +280,10 @@ public class DefaultContract : IContract return Data; } + /// + public bool HasReachedState(TState state) + => _error is not null || _resultError is not null || CurrentState.CompareTo(state) >= 0; + private async Task> SetupNewState(TAny data, CancellationToken ct) { if (_fillAtState.CompareTo(CurrentState) == 0) @@ -304,7 +329,7 @@ public class DefaultContract : IContract if (_timeouts.ContainsKey(CurrentState)) { var currentState = CurrentState; - var (timeout, state) = _timeouts[CurrentState]; + var (timeout, state, error) = _timeouts[CurrentState]; Task.Run ( @@ -314,8 +339,16 @@ public class DefaultContract : IContract if (CurrentState.CompareTo(currentState) == 0) { - await SetCurrentState(state); - await SetupNewState(null!, default); + if (state is not null) + { + await SetCurrentState(state.Value); + await SetupNewState(null!, default); + } + else if (error is not null) + { + _error = error; + _waitCancellationSource?.Cancel(); + } } } ); diff --git a/Core/NosSmooth.Core/Contracts/IContract.cs b/Core/NosSmooth.Core/Contracts/IContract.cs index 31b0968..4e30898 100644 --- a/Core/NosSmooth.Core/Contracts/IContract.cs +++ b/Core/NosSmooth.Core/Contracts/IContract.cs @@ -116,4 +116,11 @@ public interface IContract : IContract /// The data of the contract or an error. /// Thrown in case the given state cannot fill the data. public Task> WaitForAsync(TState state, bool unregisterAfter = true, CancellationToken ct = default); + + /// + /// Gets whether the given state has been reached already. + /// + /// The state to check has been reached. + /// True in case the given state has been reached or there was an error. + public bool HasReachedState(TState state); } \ No newline at end of file -- 2.49.0