DotNext by .NET Foundation and Contributors

<PackageReference Include="DotNext" Version="4.8.0" />

 GCNotification

public abstract class GCNotification
Provides a way to receive notifications from Garbage Collector asynchronously.
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace DotNext.Runtime { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public abstract class GCNotification { private sealed class MemoryThresholdFilter : GCNotification { private readonly double threshold; internal MemoryThresholdFilter(double threshold) { this.threshold = threshold; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return (double)info.MemoryLoadBytes <= (double)info.HighMemoryLoadThresholdBytes * threshold; } } private sealed class HeapCompactionFilter : GCNotification { internal static readonly HeapCompactionFilter Instance = new HeapCompactionFilter(); private HeapCompactionFilter() { } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return info.Compacted; } } private sealed class GCEvent : GCNotification { internal static readonly GCEvent Instance = new GCEvent(); private GCEvent() { } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return true; } public override GCNotification And(GCNotification right) { return right; } public override GCNotification Or(GCNotification right) { return this; } } private sealed class GenerationFilter : GCNotification { private readonly int generation; internal GenerationFilter(int generation) { this.generation = generation; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return info.Generation == generation; } } private sealed class HeapFragmentationThresholdFilter : GCNotification { private readonly double fragmentationPercentage; internal HeapFragmentationThresholdFilter(double fragmentation) { fragmentationPercentage = fragmentation; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return (double)info.FragmentedBytes / (double)info.HeapSizeBytes >= fragmentationPercentage; } } private sealed class AndFilter : GCNotification { private readonly GCNotification left; private readonly GCNotification right; internal AndFilter(GCNotification left, GCNotification right) { this.left = left; this.right = right; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { if (left.Test(ref info)) return right.Test(ref info); return false; } } private sealed class GCOrFilter : GCNotification { private readonly GCNotification left; private readonly GCNotification right; internal GCOrFilter(GCNotification left, GCNotification right) { this.left = left; this.right = right; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { if (!left.Test(ref info)) return right.Test(ref info); return true; } } private sealed class GCXorFilter : GCNotification { private readonly GCNotification left; private readonly GCNotification right; internal GCXorFilter(GCNotification left, GCNotification right) { this.left = left; this.right = right; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return left.Test(ref info) ^ right.Test(ref info); } } private sealed class GCNotFilter : GCNotification { private readonly GCNotification filter; internal GCNotFilter(GCNotification filter) { this.filter = filter; } internal override bool Test([In] [IsReadOnly] GCMemoryInfo info) { return !filter.Test(ref info); } public override GCNotification Negate() { return filter; } } private sealed class Tracker : TaskCompletionSource<GCMemoryInfo>, IGCCallback { private readonly GCNotification filter; internal Tracker(GCNotification filter) : base(TaskCreationOptions.RunContinuationsAsynchronously) { this.filter = filter; } ~Tracker() { GCMemoryInfo info = GC.GetGCMemoryInfo(); if (filter.Test(ref info)) TrySetResult(info); else GC.ReRegisterForFinalize(this); } } private abstract class GCCallback<T> { private readonly Action<T, GCMemoryInfo> callback; private readonly T state; internal GCMemoryInfo MemoryInfo; private protected GCCallback(Action<T, GCMemoryInfo> callback, T state) { this.callback = callback; this.state = state; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private protected void Execute() { callback(state, MemoryInfo); } internal abstract void Enqueue(); [MethodImpl(MethodImplOptions.NoInlining)] private protected static void UnsafeExecute(object state) { Unsafe.As<GCCallback<T>>(state).Execute(); } } private sealed class UnsafeCallback<T> : GCCallback<T>, IThreadPoolWorkItem { internal UnsafeCallback(Action<T, GCMemoryInfo> callback, T state) : base(callback, state) { } void IThreadPoolWorkItem.Execute() { Execute(); } internal override void Enqueue() { ThreadPool.UnsafeQueueUserWorkItem(this, false); } } private sealed class SynchronizationContextBoundCallback<T> : GCCallback<T> { private static readonly SendOrPostCallback CallbackInvoker = GCCallback<T>.UnsafeExecute; private readonly SynchronizationContext context; internal SynchronizationContextBoundCallback(Action<T, GCMemoryInfo> callback, T state, SynchronizationContext context) : base(callback, state) { this.context = context; } internal override void Enqueue() { context.Post(CallbackInvoker, this); } } private sealed class TaskSchedulerBoundCallback<T> : GCCallback<T> { private static readonly Action<object> TaskInvoker = GCCallback<T>.UnsafeExecute; private readonly Task task; private readonly TaskScheduler scheduler; internal TaskSchedulerBoundCallback(Action<T, GCMemoryInfo> callback, T state, TaskScheduler scheduler) : base(callback, state) { this.scheduler = scheduler; task = CreateCallbackInvocationTask(this); } internal static Task CreateCallbackInvocationTask(GCCallback<T> callback) { return new Task(TaskInvoker, callback, CancellationToken.None, TaskCreationOptions.DenyChildAttach); } internal override void Enqueue() { task.Start(scheduler); } } private abstract class ExecutionContextBoundCallback<T> : GCCallback<T> { private readonly ExecutionContext context; private protected abstract ContextCallback Callback { get; } private protected ExecutionContextBoundCallback(Action<T, GCMemoryInfo> callback, T state, ExecutionContext context) : base(callback, state) { this.context = context; } internal sealed override void Enqueue() { ExecutionContext.Run(context, Callback, this); } } private sealed class SafeCallback<T> : ExecutionContextBoundCallback<T> { private static readonly Action<object> WorkItem; private static readonly ContextCallback ContextCallback; private protected override ContextCallback Callback => ContextCallback; static SafeCallback() { WorkItem = GCCallback<T>.UnsafeExecute; ContextCallback = delegate(object callback) { ThreadPool.QueueUserWorkItem<object>(WorkItem, callback, false); }; } internal SafeCallback(Action<T, GCMemoryInfo> callback, T state, ExecutionContext context) : base(callback, state, context) { } } private sealed class ExecutionAndSynchronizationContextBoundCallback<T> : ExecutionContextBoundCallback<T> { private static readonly SendOrPostCallback CallbackInvoker; private static readonly ContextCallback ContextCallback; private readonly SynchronizationContext context; private protected override ContextCallback Callback => ContextCallback; static ExecutionAndSynchronizationContextBoundCallback() { CallbackInvoker = GCCallback<T>.UnsafeExecute; ContextCallback = delegate(object callback) { Unsafe.As<ExecutionAndSynchronizationContextBoundCallback<T>>(callback).Post(); }; } internal ExecutionAndSynchronizationContextBoundCallback(Action<T, GCMemoryInfo> callback, T state, ExecutionContext context, SynchronizationContext syncContext) : base(callback, state, context) { this.context = syncContext; } private void Post() { context.Post(CallbackInvoker, this); } } private sealed class ExecutionContextAndTaskSchedulerBoundCallback<T> : ExecutionContextBoundCallback<T> { private static readonly ContextCallback ContextCallback; private readonly TaskScheduler scheduler; private readonly Task task; private protected override ContextCallback Callback => ContextCallback; static ExecutionContextAndTaskSchedulerBoundCallback() { ContextCallback = delegate(object callback) { Unsafe.As<ExecutionContextAndTaskSchedulerBoundCallback<T>>(callback).Start(); }; } internal ExecutionContextAndTaskSchedulerBoundCallback(Action<T, GCMemoryInfo> callback, T state, ExecutionContext context, TaskScheduler scheduler) : base(callback, state, context) { this.scheduler = scheduler; task = TaskSchedulerBoundCallback<T>.CreateCallbackInvocationTask(this); } private void Start() { task.Start(scheduler); } } private sealed class Tracker<T> : IGCCallback { private readonly GCNotification filter; private readonly GCCallback<T> callback; internal Tracker(GCNotification filter, T state, Action<T, GCMemoryInfo> callback, bool continueOnCapturedContext) { this.filter = filter; ExecutionContext executionContext = ExecutionContext.Capture(); if (!continueOnCapturedContext) this.callback = ((executionContext == null) ? ((GCCallback<T>)new UnsafeCallback<T>(callback, state)) : ((GCCallback<T>)new SafeCallback<T>(callback, state, executionContext))); else { SynchronizationContext current = SynchronizationContext.Current; if (current != null && current.GetType() != typeof(SynchronizationContext)) this.callback = ((executionContext == null) ? ((GCCallback<T>)new SynchronizationContextBoundCallback<T>(callback, state, current)) : ((GCCallback<T>)new ExecutionAndSynchronizationContextBoundCallback<T>(callback, state, executionContext, current))); else this.callback = ((executionContext == null) ? ((GCCallback<T>)new TaskSchedulerBoundCallback<T>(callback, state, TaskScheduler.Current)) : ((GCCallback<T>)new ExecutionContextAndTaskSchedulerBoundCallback<T>(callback, state, executionContext, TaskScheduler.Current))); } } ~Tracker() { GCMemoryInfo info = GC.GetGCMemoryInfo(); if (filter.Test(ref info)) { callback.MemoryInfo = info; callback.Enqueue(); } else GC.ReRegisterForFinalize(this); } } [StructLayout(LayoutKind.Auto)] [System.Runtime.CompilerServices.NullableContext(0)] public readonly struct Registration : IDisposable { private readonly GCIntermediateReference reference; internal Registration(IGCCallback callback) { reference = new GCIntermediateReference(callback); } public void Dispose() { reference?.Clear(); } } public Registration Register<[System.Runtime.CompilerServices.Nullable(2)] T>(Action<T, GCMemoryInfo> callback, T state, bool captureContext = false) { ArgumentNullException.ThrowIfNull((object)callback, "callback"); return new Registration(new Tracker<T>(this, state, callback, captureContext)); } public Task<GCMemoryInfo> WaitAsync(TimeSpan timeout, CancellationToken token = default(CancellationToken)) { Task<GCMemoryInfo> task; if (token.IsCancellationRequested) task = Task.FromCanceled<GCMemoryInfo>(token); else { Tracker tracker = new Tracker(this); task = tracker.Task.WaitAsync(timeout, token); task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(new GCIntermediateReference(tracker).Clear); GC.KeepAlive(tracker); } return task; } public Task<GCMemoryInfo> WaitAsync(CancellationToken token = default(CancellationToken)) { return WaitAsync(Timeout.InfiniteTimeSpan, token); } public static GCNotification HeapCompaction() { return HeapCompactionFilter.Instance; } public static GCNotification GCTriggered() { return GCEvent.Instance; } public static GCNotification GCTriggered(int generation) { if (generation < 0 || generation > GC.MaxGeneration) throw new ArgumentOutOfRangeException("generation"); return new GenerationFilter(generation); } public static GCNotification HeapFragmentation(double threshold) { if (!double.IsFinite(threshold) || !threshold.IsBetween(0, 1, BoundType.RightClosed)) throw new ArgumentOutOfRangeException("threshold"); return new HeapFragmentationThresholdFilter(threshold); } public static GCNotification MemoryThreshold(double threshold) { if (!double.IsFinite(threshold) || !threshold.IsBetween(0, 1, BoundType.RightClosed)) throw new ArgumentOutOfRangeException("threshold"); return new MemoryThresholdFilter(threshold); } private protected GCNotification() { } internal abstract bool Test([In] [IsReadOnly] GCMemoryInfo info); public virtual GCNotification And(GCNotification right) { ArgumentNullException.ThrowIfNull((object)right, "right"); return new AndFilter(this, right); } public virtual GCNotification Or(GCNotification right) { ArgumentNullException.ThrowIfNull((object)right, "right"); return new GCOrFilter(this, right); } public virtual GCNotification ExclusiveOr(GCNotification right) { ArgumentNullException.ThrowIfNull((object)right, "right"); return new GCXorFilter(this, right); } public virtual GCNotification Negate() { return new GCNotFilter(this); } public static GCNotification operator &(GCNotification left, GCNotification right) { return left.And(right); } public static GCNotification operator |(GCNotification left, GCNotification right) { return left.Or(right); } public static GCNotification operator ^(GCNotification left, GCNotification right) { return left.ExclusiveOr(right); } public static GCNotification operator !(GCNotification filter) { return filter.Negate(); } } }