private static async Task WorkerAsync(
            BlockingCollection <SentryEvent> queue,
            BackgroundWorkerOptions options,
            ITransport transport,
            CancellationToken cancellation)
        {
            var shutdownTimeout   = new CancellationTokenSource();
            var shutdownRequested = false;

            while (!shutdownTimeout.IsCancellationRequested)
            {
                // If the cancellation was signaled,
                // set the latest we can keep reading off the queue (while there's still stuff to read)
                if (!shutdownRequested && cancellation.IsCancellationRequested)
                {
                    shutdownTimeout.CancelAfter(options.ShutdownTimeout);
                    shutdownRequested = true;
                }

                if (queue.TryTake(out var @event))
                {
                    try
                    {
                        // Optionally we can keep multiple requests in-flight concurrently:
                        // instead of awaiting here, keep reading from the queue while less than
                        // N events are being sent
                        await transport.CaptureEventAsync(@event, shutdownTimeout.Token).ConfigureAwait(false);
                    }
                    catch (Exception exception)
                    {
                        // TODO: Notify error handler
                        Debug.WriteLine(exception.ToString());
                    }
                }
                else
                {
                    if (shutdownRequested)
                    {
                        break; // Shutdown requested and queue is empty. ready to quit.
                    }

                    // Queue is empty, wait asynchronously before polling again
                    try
                    {
                        await Task.Delay(options.EmptyQueueDelay, cancellation).ConfigureAwait(false);
                    }
                    // Cancellation requested, scheduled shutdown but loop again in case there are more items
                    catch (OperationCanceledException)
                    {
                        shutdownTimeout.CancelAfter(options.ShutdownTimeout);
                        shutdownRequested = true;
                    }
                }
            }
        }
Esempio n. 2
0
        private static async Task WorkerAsync(
            IProducerConsumerCollection <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.TryTake(out var @event))
                    {
                        inSemaphore.Release();
                        try
                        {
                            // Optionally we can keep multiple requests in-flight concurrently:
                            // instead of awaiting here, keep reading from the queue while less than
                            // N events are being sent
                            var task = transport.CaptureEventAsync(@event, shutdownTimeout.Token).ConfigureAwait(false);
                            options.DiagnosticLogger?.LogDebug("Event {0} in-flight to Sentry. #{1} in queue.", @event.EventId, queue.Count);
                            await task;
                        }
                        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);
                        }
                    }
                    else
                    {
                        Debug.Assert(shutdownRequested);
                        options.DiagnosticLogger?.LogInfo("Exiting the worker with an empty queue.");

                        // Empty queue. Exit.
                        return;
                    }
                }
            }
            finally
            {
                inSemaphore.Dispose();
                outSemaphore.Dispose();
            }
        }
Esempio n. 3
0
        private async Task WorkerAsync(
            ConcurrentQueue <SentryEvent> queue,
            SentryOptions options,
            ITransport transport,
            SemaphoreSlim queuedEventSemaphore,
            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 queuedEventSemaphore (Enqueue will throw object disposed),
                    // run until the end of the queue or shutdownTimeout
                    if (!shutdownRequested)
                    {
                        try
                        {
                            await queuedEventSemaphore.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 _);
                            _ = Interlocked.Decrement(ref _currentItems);
                            OnFlushObjectReceived?.Invoke(@event, 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
            {
                queuedEventSemaphore.Dispose();
            }
        }
Esempio n. 4
0
        private static async Task WorkerAsync(
            IProducerConsumerCollection <SentryEvent> queue,
            BackgroundWorkerOptions 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)
                            {
                                return;
                            }
                            else
                            {
                                shutdownTimeout.CancelAfter(options.ShutdownTimeout);
                            }

                            shutdownRequested = true;
                        }
                    }

                    if (queue.TryTake(out var @event))
                    {
                        inSemaphore.Release();
                        try
                        {
                            // Optionally we can keep multiple requests in-flight concurrently:
                            // instead of awaiting here, keep reading from the queue while less than
                            // N events are being sent
                            await transport.CaptureEventAsync(@event, shutdownTimeout.Token).ConfigureAwait(false);
                        }
                        catch (OperationCanceledException)
                        {
                            // Shutdown token triggered. Time to exit.
                            return;
                        }
                        catch (Exception exception)
                        {
                            // TODO: Notify error handler
                            Debug.WriteLine(exception.ToString());
                        }
                    }
                    else
                    {
                        Debug.Assert(shutdownRequested);

                        // Empty queue. Exit.
                        return;
                    }
                }
            }
            finally
            {
                inSemaphore.Dispose();
                outSemaphore.Dispose();
            }
        }