private async Task WorkerAsync( ConcurrentQueue <SentryEvent> queue, SentryOptions options, ITransport transport, SemaphoreSlim inSemaphore, SemaphoreSlim outSemaphore, CancellationToken cancellation) { var shutdownTimeout = new CancellationTokenSource(); var shutdownRequested = false; try { while (!shutdownTimeout.IsCancellationRequested) { // If the cancellation was signaled, // set the latest we can keep reading off of the queue (while there's still stuff to read) // No longer synchronized with outSemaphore (Enqueue will throw object disposed), // run until the end of the queue or shutdownTimeout if (!shutdownRequested) { try { await outSemaphore.WaitAsync(cancellation).ConfigureAwait(false); } // Cancellation requested, scheduled shutdown but continue in case there are more items catch (OperationCanceledException) { if (options.ShutdownTimeout == TimeSpan.Zero) { options.DiagnosticLogger?.LogDebug("Exiting immediately due to 0 shutdown timeout. #{0} in queue.", queue.Count); return; } else { options.DiagnosticLogger?.LogDebug("Shutdown scheduled. Stopping by: {0}. #{1} in queue.", options.ShutdownTimeout, queue.Count); shutdownTimeout.CancelAfter(options.ShutdownTimeout); } shutdownRequested = true; } } if (queue.TryPeek(out var @event)) // Work with the event while it's in the queue { try { var task = transport.CaptureEventAsync(@event, shutdownTimeout.Token); options.DiagnosticLogger?.LogDebug("Event {0} in-flight to Sentry. #{1} in queue.", @event.EventId, queue.Count); await task.ConfigureAwait(false); } catch (OperationCanceledException) { options.DiagnosticLogger?.LogInfo("Shutdown token triggered. Time to exit. #{0} in queue.", queue.Count); return; } catch (Exception exception) { options.DiagnosticLogger?.LogError("Error while processing event {1}: {0}. #{2} in queue.", exception, @event.EventId, queue.Count); } finally { queue.TryDequeue(out _); inSemaphore.Release(); OnFlushObjectReceived?.Invoke(@event, EventArgs.Empty); } } else { Debug.Assert(shutdownRequested); options.DiagnosticLogger?.LogInfo("Exiting the worker with an empty queue."); // Empty queue. Exit. return; } } } finally { inSemaphore.Dispose(); outSemaphore.Dispose(); } }
private async Task WorkerAsync() { var cancellation = _shutdownSource.Token; using var shutdownTimeout = new CancellationTokenSource(); var shutdownRequested = false; try { while (!shutdownTimeout.IsCancellationRequested) { // If the cancellation was signaled, // set the latest we can keep reading off of the queue (while there's still stuff to read) // No longer synchronized with queuedEnvelopeSemaphore (Enqueue will throw object disposed), // run until the end of the queue or shutdownTimeout if (!shutdownRequested) { try { await _queuedEnvelopeSemaphore.WaitAsync(cancellation).ConfigureAwait(false); } // Cancellation requested, scheduled shutdown but continue in case there are more items catch (OperationCanceledException) { if (_options.ShutdownTimeout == TimeSpan.Zero) { _options.DiagnosticLogger?.LogDebug( "Exiting immediately due to 0 shutdown timeout. #{0} in queue.", _queue.Count ); return; } else { _options.DiagnosticLogger?.LogDebug( "Shutdown scheduled. Stopping by: {0}. #{1} in queue.", _options.ShutdownTimeout, _queue.Count ); shutdownTimeout.CancelAfter(_options.ShutdownTimeout); } shutdownRequested = true; } } if (_queue.TryPeek(out var envelope)) // Work with the envelope while it's in the queue { try { var task = _transport.SendEnvelopeAsync(envelope, shutdownTimeout.Token); _options.DiagnosticLogger?.LogDebug( "Envelope {0} in-flight to Sentry. #{1} in queue.", envelope.TryGetEventId(), _queue.Count ); await task.ConfigureAwait(false); } catch (OperationCanceledException) { _options.DiagnosticLogger?.LogInfo( "Shutdown token triggered. Time to exit. #{0} in queue.", _queue.Count ); return; } catch (Exception exception) { _options.DiagnosticLogger?.LogError( "Error while processing event {1}: {0}. #{2} in queue.", exception, envelope.TryGetEventId(), _queue.Count ); } finally { _ = _queue.TryDequeue(out _); _ = Interlocked.Decrement(ref _currentItems); OnFlushObjectReceived?.Invoke(envelope, EventArgs.Empty); } } else { Debug.Assert(shutdownRequested); _options.DiagnosticLogger?.LogInfo("Exiting the worker with an empty queue."); // Empty queue. Exit. return; } } } catch (Exception e) { _options.DiagnosticLogger?.LogFatal("Exception in the background worker.", e); throw; } finally { _queuedEnvelopeSemaphore.Dispose(); } }
private async Task WorkerAsync(CancellationToken cancellation) { var shutdownTimeout = new CancellationTokenSource(); var shutdownRequested = false; try { while (!shutdownTimeout.IsCancellationRequested) { if (!shutdownRequested) { try { await _queuedEventSemaphore.WaitAsync(cancellation).ConfigureAwait(false); } catch (OperationCanceledException) { if (_options.ShutdownTimeout == TimeSpan.Zero) { return; } shutdownTimeout.CancelAfter(_options.ShutdownTimeout); shutdownRequested = true; } } if (_queue.TryPeek(out var exceptionInstance)) { try { var task = _excepticonExceptionInstancesService.PostExceptionInstance(exceptionInstance, _options.ApiKey); await task.ConfigureAwait(false); } catch (OperationCanceledException) { return; } catch (Exception e) { Debug.WriteLine(e.Message); } finally { _queue.TryDequeue(out exceptionInstance); Interlocked.Decrement(ref _currentItems); OnFlushObjectReceived?.Invoke(exceptionInstance, EventArgs.Empty); } } else { return; } } } catch (Exception e) { Console.WriteLine(e); throw; } finally { _queuedEventSemaphore.Dispose(); } }