You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.8 KiB
134 lines
3.8 KiB
5 years ago
|
// 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
|