// Needed for Workaround #if !NET_4_6 using System; using System.Threading; namespace LinqInternal.Threading { [System.Diagnostics.DebuggerNonUserCode] internal static partial class GCMonitor { private const int _maxProbingHint = 128; private const int _statusFinished = 1; private const int _statusNotReady = -2; private const int _statusPending = -1; private const int _statusReady = 0; private static int _status = _statusNotReady; #if !NETCOREAPP1_1 static GCMonitor() { var currentAppDomain = AppDomain.CurrentDomain; currentAppDomain.ProcessExit += ReportApplicationDomainExit; currentAppDomain.DomainUnload += ReportApplicationDomainExit; } #endif public static event EventHandler Collected { add { try { Initialize(); Internal.CollectedEventHandlers.Add(value); } catch { if (ReferenceEquals(value, null)) { return; } throw; } } remove { if (Volatile.Read(ref _status) == _statusReady) { try { Internal.CollectedEventHandlers.Remove(value); } catch { if (ReferenceEquals(value, null)) { return; } throw; } } } } public static bool FinalizingForUnload { get { // If you need to get rid of this, just set this property to return false #if !NETCOREAPP1_1 return AppDomain.CurrentDomain.IsFinalizingForUnload(); #else return false; #endif } } private static void Initialize() { var check = Interlocked.CompareExchange(ref _status, _statusPending, _statusNotReady); switch (check) { case _statusNotReady: GC.KeepAlive(new GCProbe()); Volatile.Write(ref _status, _statusReady); break; case _statusPending: ThreadingHelper.SpinWaitUntil(ref _status, _statusReady); break; } } #if !NETCOREAPP1_1 private static void ReportApplicationDomainExit(object sender, EventArgs e) { Volatile.Write(ref _status, _statusFinished); } #endif [System.Diagnostics.DebuggerNonUserCode] private sealed class GCProbe #if !NETCOREAPP1_1 : System.Runtime.ConstrainedExecution.CriticalFinalizerObject #endif { ~GCProbe() { try { // Empty } finally { try { var check = Volatile.Read(ref _status); if (check == _statusReady) { GC.ReRegisterForFinalize(this); Internal.Invoke(); } } catch (Exception exception) { // Catch'em all - there shouldn't be exceptions here, yet we really don't want them GC.KeepAlive(exception); } } } } } } #endif