From 294f2a3c61786e65169f7d410ba88070bd0f4821 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 72d92119acd795..0b55c536adcfdc 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 @@ -39,6 +39,7 @@ public class MemoryCache : ObjectCache, IEnumerable, IDisposable private bool _useMemoryCacheManager = true; private EventHandler _onAppDomainUnload; private UnhandledExceptionEventHandler _onUnhandledException; + private int _inUnhandledExceptionHandler; #if NET5_0_OR_GREATER [UnsupportedOSPlatformGuard("browser")] private static bool _countersSupported => !OperatingSystem.IsBrowser(); @@ -240,14 +241,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 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 107c46f0158c9c..1ad5d6c2ff65a4 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 @@ -334,8 +334,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 b0027403163355..5d7eaa6a96fdc1 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 @@ -56,6 +56,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 667d498f2d6c3454c1f73e73e01343a30f48c3a8 Mon Sep 17 00:00:00 2001 From: Steve Molloy Date: Tue, 16 Jul 2024 11:30:36 -0700 Subject: [PATCH 2/2] Update Sys.RT.Caching csproj for servicing build. --- .../System.Runtime.Caching/src/System.Runtime.Caching.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 3f8f859c4f45d9..ef2dd3aa7ed4ab 100644 --- a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj +++ b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj @@ -4,8 +4,8 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0-windows;netstandard2.0 true - false - 0 + true + 1 true true true