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
// 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 |