From 43a164f42fe0bc8776687d906ad07f0a707a87fb Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Thu, 11 Jul 2024 11:16:28 -0700 Subject: [PATCH 1/2] Don't dispose timers if we're in our UnhandledException handler. (#103937) --- .../src/System/Runtime/Caching/MemoryCache.cs | 6 ++++++ .../Runtime/Caching/MemoryCacheStatistics.cs | 15 +++++++++++++-- .../src/System/Runtime/Caching/SRef.cs | 5 +++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs index e740eed69ed514..e2c99f5a92e050 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs @@ -40,6 +40,7 @@ public class MemoryCache : ObjectCache, IEnumerable, IDisposable private bool _throwOnDisposed; private EventHandler _onAppDomainUnload; private UnhandledExceptionEventHandler _onUnhandledException; + private int _inUnhandledExceptionHandler; #if NETCOREAPP [UnsupportedOSPlatformGuard("browser")] private static bool _countersSupported => !OperatingSystem.IsBrowser(); @@ -238,14 +239,19 @@ private void OnAppDomainUnload(object unusedObject, EventArgs unusedEventArgs) Dispose(); } + internal bool InUnhandledExceptionHandler => _inUnhandledExceptionHandler > 0; private void OnUnhandledException(object sender, UnhandledExceptionEventArgs eventArgs) { + Interlocked.Increment(ref _inUnhandledExceptionHandler); + // if the CLR is terminating, dispose the cache. // This will dispose the perf counters if (eventArgs.IsTerminating) { Dispose(); } + + Interlocked.Decrement(ref _inUnhandledExceptionHandler); } private static void ValidatePolicy(CacheItemPolicy policy) diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs index 96cfc3bda655bd..0599e8febc593c 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs @@ -340,8 +340,19 @@ public void Dispose() GCHandleRef timerHandleRef = _timerHandleRef; if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) { - timerHandleRef.Dispose(); - Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers"); + // If inside an unhandled exception handler, Timers may be succeptible to deadlocks. Use a safer approach. + if (_memoryCache.InUnhandledExceptionHandler) + { + // This does not stop/dispose the timer. But the callback on the timer is protected by _disposed, which we have already + // set above. + timerHandleRef.FreeHandle(); + Dbg.Trace("MemoryCacheStats", "Freed CacheMemoryTimers"); + } + else + { + timerHandleRef.Dispose(); + Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers"); + } } } while (_inCacheManagerThread != 0) diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs index 9a09ba1d9781ae..412f66971a7fea 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs @@ -59,6 +59,11 @@ public T Target public void Dispose() { Target.Dispose(); + FreeHandle(); + } + + internal void FreeHandle() + { // Safe to call Dispose more than once but not thread-safe if (_handle.IsAllocated) { From e9d7d615666906ab24b8d65a68ec194ade2a95d0 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 16 Jul 2024 11:23:35 -0700 Subject: [PATCH 2/2] Update Sys.RT.Caching csproj for servicing build. --- .../System.Runtime.Caching/src/System.Runtime.Caching.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj index b3d85127f02129..241cbbffb21367 100644 --- a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj +++ b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj @@ -3,6 +3,8 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);$(NetCoreAppPrevious)-windows;$(NetCoreAppPrevious);$(NetCoreAppMinimum)-windows;$(NetCoreAppMinimum);netstandard2.0 true true + true + 1 true true true