diff --git a/src/Sentry.Profiling/SamplingTransactionProfilerFactory.cs b/src/Sentry.Profiling/SamplingTransactionProfilerFactory.cs index 5f0d54453d..386fd1f4f2 100644 --- a/src/Sentry.Profiling/SamplingTransactionProfilerFactory.cs +++ b/src/Sentry.Profiling/SamplingTransactionProfilerFactory.cs @@ -1,3 +1,4 @@ +using Sentry.Ben.BlockingDetector; using Sentry.Extensibility; using Sentry.Internal; @@ -27,16 +28,19 @@ public SamplingTransactionProfilerFactory(SentryOptions options, TimeSpan startu { _options = options; - _sessionTask = Task.Run(async () => + using (new SuppressBlockingDetection()) { - // This can block up to 30 seconds. The timeout is out of our hands. - var session = SampleProfilerSession.StartNew(options.DiagnosticLogger); + _sessionTask = Task.Run(async () => + { + // This can block up to 30 seconds. The timeout is out of our hands. + var session = SampleProfilerSession.StartNew(options.DiagnosticLogger); - // This can block indefinitely. - await session.WaitForFirstEventAsync().ConfigureAwait(false); + // This can block indefinitely. + await session.WaitForFirstEventAsync().ConfigureAwait(false); - return session; - }); + return session; + }); + } Debug.Assert(TimeSpan.FromSeconds(0) == TimeSpan.Zero); if (startupTimeout != TimeSpan.Zero && !_sessionTask.Wait(startupTimeout)) diff --git a/src/Sentry/Internal/BackgroundWorker.cs b/src/Sentry/Internal/BackgroundWorker.cs index d747922d5a..f7a283f3ed 100644 --- a/src/Sentry/Internal/BackgroundWorker.cs +++ b/src/Sentry/Internal/BackgroundWorker.cs @@ -1,3 +1,4 @@ +using Sentry.Ben.BlockingDetector; using Sentry.Extensibility; using Sentry.Internal.Extensions; using Sentry.Internal.Http; @@ -37,7 +38,10 @@ public BackgroundWorker( _queuedEnvelopeSemaphore = new SemaphoreSlim(0, _maxItems); options.LogDebug("Starting BackgroundWorker."); - WorkerTask = Task.Run(DoWorkAsync); + using (new SuppressBlockingDetection()) + { + WorkerTask = Task.Run(DoWorkAsync); + } } /// diff --git a/src/Sentry/Internal/GarbageCollectionMonitor.cs b/src/Sentry/Internal/GarbageCollectionMonitor.cs index a17486b8fd..825f628172 100644 --- a/src/Sentry/Internal/GarbageCollectionMonitor.cs +++ b/src/Sentry/Internal/GarbageCollectionMonitor.cs @@ -1,3 +1,4 @@ +using Sentry.Ben.BlockingDetector; using Sentry.Extensibility; namespace Sentry.Internal; @@ -10,8 +11,13 @@ internal sealed class GarbageCollectionMonitor private const int MaxGenerationThreshold = 10; private const int LargeObjectHeapThreshold = 10; - public static Task Start(Action onGarbageCollected, CancellationToken cancellationToken, IGCImplementation? gc = null) => - Task.Run(() => MonitorGarbageCollection(onGarbageCollected, cancellationToken, gc), cancellationToken); + public static Task Start(Action onGarbageCollected, CancellationToken cancellationToken, IGCImplementation? gc = null) + { + using (new SuppressBlockingDetection()) + { + return Task.Run(() => MonitorGarbageCollection(onGarbageCollected, cancellationToken, gc), cancellationToken); + } + } private static void MonitorGarbageCollection(Action onGarbageCollected, CancellationToken cancellationToken, IGCImplementation? gc = null) { diff --git a/src/Sentry/Internal/Http/CachingTransport.cs b/src/Sentry/Internal/Http/CachingTransport.cs index b5ed2cb34a..039226d7d0 100644 --- a/src/Sentry/Internal/Http/CachingTransport.cs +++ b/src/Sentry/Internal/Http/CachingTransport.cs @@ -1,3 +1,5 @@ +using System.Text.Json; +using Sentry.Ben.BlockingDetector; using Sentry.Extensibility; using Sentry.Internal.Extensions; using Sentry.Protocol.Envelopes; @@ -98,7 +100,10 @@ private void Initialize(bool startWorker) if (startWorker) { _options.LogDebug("Starting CachingTransport worker."); - _worker = Task.Run(CachedTransportBackgroundTaskAsync); + using (new SuppressBlockingDetection()) + { + _worker = Task.Run(CachedTransportBackgroundTaskAsync); + } } else { diff --git a/src/Sentry/Internal/ProcessInfo.cs b/src/Sentry/Internal/ProcessInfo.cs index 414c93e394..65d3bc13b8 100644 --- a/src/Sentry/Internal/ProcessInfo.cs +++ b/src/Sentry/Internal/ProcessInfo.cs @@ -1,3 +1,4 @@ +using Sentry.Ben.BlockingDetector; using Sentry.Extensibility; namespace Sentry.Internal; @@ -95,20 +96,23 @@ internal ProcessInfo( // This method will give a better precision to the StartupTime at a cost // of calling Process.GetCurrentProcess, on a thread pool thread. var preciseStartupTimeFunc = findPreciseStartupTime ?? GetStartupTime; - PreciseAppStartupTask = Task.Run(() => + using (new SuppressBlockingDetection()) { - try + PreciseAppStartupTask = Task.Run(() => { - StartupTime = preciseStartupTimeFunc(); - } - catch (Exception e) - { - options.LogError(e, "Failure getting precise App startup time."); - //Ignore any exception and stay with the less-precise DateTime.UtcNow value. - } - }).ContinueWith(_ => - // Let the actual task get collected - PreciseAppStartupTask = Task.CompletedTask); + try + { + StartupTime = preciseStartupTimeFunc(); + } + catch (Exception e) + { + options.LogError(e, "Failure getting precise App startup time."); + //Ignore any exception and stay with the less-precise DateTime.UtcNow value. + } + }).ContinueWith(_ => + // Let the actual task get collected + PreciseAppStartupTask = Task.CompletedTask); + } #endif } } diff --git a/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs b/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs index d506a179e6..b52b468a76 100644 --- a/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs +++ b/test/Sentry.Tests/Internals/BackgroundWorkerTests.cs @@ -1,4 +1,5 @@ using System.IO.Abstractions.TestingHelpers; +using Sentry.Ben.BlockingDetector; using Sentry.Internal.Http; using BackgroundWorker = Sentry.Internal.BackgroundWorker; @@ -521,4 +522,38 @@ public async Task FlushAsync_Calls_CachingTransport_FlushAsync() _fixture.Logger.Received(1) .Log(SentryLevel.Debug, "CachingTransport received request to flush the cache."); } + + [Fact] + public void Ctor_SuppressesBlockingDetection_WhenCreatingTask() + { + // Arrange + var listenerState = Substitute.For(); + var monitor = Substitute.For(); + var context = new DetectBlockingSynchronizationContext(monitor); + + // Mock the current sync context to our test context + SynchronizationContext.SetSynchronizationContext(context); + + // Mock the TaskBlockingListener to return our test state + var originalDefaultState = TaskBlockingListener.DefaultState; + TaskBlockingListener.DefaultState = listenerState; + + try + { + // Act + using var sut = _fixture.GetSut(); + + // Assert + // Verify that suppression was called when creating the task + listenerState.Received(1).Suppress(); + // Verify that restoration was called when exiting the using block + listenerState.Received(1).Restore(); + } + finally + { + // Cleanup + TaskBlockingListener.DefaultState = originalDefaultState; + SynchronizationContext.SetSynchronizationContext(null); + } + } }