public async Task OpenAsync() { streamRecoveredSignal = new TaskCompletionSource <EventMessageStream <T> >(TaskCreationOptions.RunContinuationsAsynchronously); // streamRecoverTimeoutSignal = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); EventMessageStream <T> newStream = null; newStream = ActiveStream = getStreamDelegate((innerEx) => HandleUnexpectedCloseAsync(newStream, innerEx)); await ActiveStream.OpenAsync().ConfigureAwait(false); await openDelegate(ActiveStream).ConfigureAwait(false); }
private ValueTask HandleUnexpectedCloseAsync(EventMessageStream <T> oldStream, Exception ex) { // we have to run this off thread because we will block the old streams close method. Task.Run(async() => { oldStream.Logger?.LogError(ex, "MessageStream errored. Attempting to recover..."); try { await closeDelegate(oldStream).ConfigureAwait(false); await Task.WhenAny(Task.Delay(oldStreamCloseTimeout), oldStream.CloseAsync()).ConfigureAwait(false); } catch (Exception closeEx) { oldStream.Logger?.LogError(closeEx, "Error closing old message stream"); } bool success = false; EventMessageStream <T> newStream = null; while (!success && numReconnectAttempts < maxReconnectAttempts) { try { await Task.Delay((int)(numReconnectAttempts * reconnectBackoff.TotalMilliseconds)).ConfigureAwait(false); numReconnectAttempts++; // we dont want to handle the disconnects until we've successfully reopened newStream = getStreamDelegate((innerEx) => success ? HandleUnexpectedCloseAsync(newStream, innerEx) : new ValueTask()); await newStream.OpenAsync().ConfigureAwait(false); await openDelegate(newStream).ConfigureAwait(false); // Success numReconnectAttempts = 0; success = true; } catch (Exception reconnectEx) { // We override the orig exception with the latest one oldStream.Logger?.LogError(ex = reconnectEx, $"Error recovering message stream after {numReconnectAttempts} attempts."); } } // if we didnt succeed then close the new stream and send the disconnect event if (!success) { streamRecoveredSignal.TrySetResult(null); try { await closeDelegate(newStream).ConfigureAwait(false); await Task.WhenAny(Task.Delay(oldStreamCloseTimeout), newStream.CloseAsync()).ConfigureAwait(false); await unexpectedCloseDelegate(ex).ConfigureAwait(false); oldStream.Logger?.LogWarning($"Closed message stream after {numReconnectAttempts} reconnection attempts."); } catch (Exception closeEx) { oldStream.Logger?.LogError(closeEx, "Error closing new message stream."); } } else { ActiveStream = newStream; streamRecoveredSignal.TrySetResult(newStream); streamRecoveredSignal = new TaskCompletionSource <EventMessageStream <T> >(TaskCreationOptions.RunContinuationsAsynchronously); newStream.Logger?.LogInformation("MessageStream recovered."); } }); return(new ValueTask()); }