@@ 29,7 29,7 @@ public class ContractBuilder<TData, TState, TError>
private readonly TState _defaultState;
private readonly Dictionary<TState, DefaultContract<TData, TState, TError>.StateActionAsync> _actions;
- private readonly Dictionary<TState, (TimeSpan, TState)> _timeouts;
+ private readonly Dictionary<TState, (TimeSpan, TState?, TError?)> _timeouts;
private TState? _fillAtState;
private DefaultContract<TData, TState, TError>.FillDataAsync? _fillData;
@@ 44,7 44,7 @@ public class ContractBuilder<TData, TState, TError>
_contractor = contractor;
_defaultState = defaultState;
_actions = new Dictionary<TState, DefaultContract<TData, TState, TError>.StateActionAsync>();
- _timeouts = new Dictionary<TState, (TimeSpan, TState)>();
+ _timeouts = new Dictionary<TState, (TimeSpan, TState?, TError?)>();
}
/// <summary>
@@ 56,7 56,20 @@ public class ContractBuilder<TData, TState, TError>
/// <returns>The updated builder.</returns>
public ContractBuilder<TData, TState, TError> SetTimeout(TState state, TimeSpan timeout, TState nextState)
{
- _timeouts[state] = (timeout, nextState);
+ _timeouts[state] = (timeout, nextState, null);
+ return this;
+ }
+
+ /// <summary>
+ /// Sets timeout of the given state.
+ /// </summary>
+ /// <param name="state">The state to set timeout for.</param>
+ /// <param name="timeout">The timeout span.</param>
+ /// <param name="error">The error to set.</param>
+ /// <returns>The updated builder.</returns>
+ public ContractBuilder<TData, TState, TError> SetTimeout(TState state, TimeSpan timeout, TError error)
+ {
+ _timeouts[state] = (timeout, null, error);
return this;
}
@@ 44,7 44,7 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
private readonly SemaphoreSlim _semaphore;
- private readonly IDictionary<TState, (TimeSpan, TState)> _timeouts;
+ private readonly IDictionary<TState, (TimeSpan, TState?, TError?)> _timeouts;
private readonly IDictionary<TState, StateActionAsync> _actions;
private readonly Contractor _contractor;
private readonly TState _defaultState;
@@ 75,7 75,7 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
TState fillAtState,
FillDataAsync fillData,
IDictionary<TState, StateActionAsync> actions,
- IDictionary<TState, (TimeSpan Timeout, TState NextState)> timeouts
+ IDictionary<TState, (TimeSpan Timeout, TState? NextState, TError? Error)> timeouts
)
{
_semaphore = new SemaphoreSlim(1, 1);
@@ 139,8 139,16 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
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<TData, TState, TError> : IContract<TData, TState>
{
_error = error;
_waitCancellationSource?.Cancel();
+ return new ContractError<TError>(error.Value);
}
if (state is not null)
@@ 199,6 208,16 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
);
}
+ if (_error is not null)
+ {
+ return new ContractError<TError>(_error.Value);
+ }
+
+ if (_resultError is not null)
+ {
+ return Result<TData>.FromError(_resultError.Value);
+ }
+
if (CurrentState.CompareTo(state) >= 0)
{ // already reached.
return Data;
@@ 234,6 253,8 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
{
Unregister();
}
+
+ _waitCancellationSource?.Dispose();
}
if (ct.IsCancellationRequested)
@@ 259,6 280,10 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
return Data;
}
+ /// <inheritdoc />
+ public bool HasReachedState(TState state)
+ => _error is not null || _resultError is not null || CurrentState.CompareTo(state) >= 0;
+
private async Task<Result<ContractUpdateResponse>> SetupNewState<TAny>(TAny data, CancellationToken ct)
{
if (_fillAtState.CompareTo(CurrentState) == 0)
@@ 304,7 329,7 @@ public class DefaultContract<TData, TState, TError> : IContract<TData, TState>
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<TData, TState, TError> : IContract<TData, TState>
if (CurrentState.CompareTo(currentState) == 0)
{
- await SetCurrentState(state);
- await SetupNewState<int?>(null!, default);
+ if (state is not null)
+ {
+ await SetCurrentState(state.Value);
+ await SetupNewState<int?>(null!, default);
+ }
+ else if (error is not null)
+ {
+ _error = error;
+ _waitCancellationSource?.Cancel();
+ }
}
}
);