File: src\runtime\src\coreclr\nativeaot\Runtime.Base\src\System\Runtime\__Finalizer.cs
Web Access
Project: src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;

//
// Implements the single finalizer thread for a NativeAOT instance. Essentially waits for an event to fire
// indicating finalization is necessary then drains the queue of pending finalizable objects, calling the
// finalize method for each one.
//

namespace System.Runtime
{
    // We choose this name to avoid clashing with any future public class with the name Finalizer.
    internal static class __Finalizer
    {
        [UnmanagedCallersOnly(EntryPoint = "ProcessFinalizers")]
        public static void ProcessFinalizers()
        {
            while (true)
            {
                // Wait until there's some work to be done. If true is returned we should finalize objects,
                // otherwise memory is low and we should initiate a collection.
                if (InternalCalls.RhpWaitForFinalizerRequest() != 0)
                {
                    int observedFullGcCount = RuntimeImports.RhGetGcCollectionCount(RuntimeImports.RhGetMaxGcGeneration(), false);
                    uint finalizerCount = DrainQueue();

                    // Anyone waiting to drain the Q can now wake up.  Note that there is a
                    // race in that another thread starting a drain, as we leave a drain, may
                    // consider itself satisfied by the drain that just completed.
                    // Thus we include the Full GC count that we have certaily observed.
                    InternalCalls.RhpSignalFinalizationComplete(finalizerCount, observedFullGcCount);
                }
                else
                {
                    // RhpWaitForFinalizerRequest() returned false and indicated that memory is low. We help
                    // out by initiating a garbage collection and then go back to waiting for another request.
                    InternalCalls.RhCollect(0, InternalGCCollectionMode.Blocking, lowMemoryP: Interop.BOOL.TRUE);
                }
            }
        }

        // Do not inline this method -- we do not want to accidentally have any temps in ProcessFinalizers which contain
        // objects that came off of the finalizer queue.  If such temps were reported across the duration of the
        // finalizer thread wait operation, it could cause unpredictable behavior with weak handles.
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static unsafe uint DrainQueue()
        {
            uint finalizerCount = 0;
            // Drain the queue of finalizable objects.
            while (true)
            {
                object target = InternalCalls.RhpGetNextFinalizableObject();
                if (target == null)
                    return finalizerCount;

                finalizerCount++;

                try
                {
                    // Call the finalizer on the current target object.
                    ((delegate*<object, void>)target.GetMethodTable()->FinalizerCode)(target);
                }
                catch (Exception ex) when (ExceptionHandling.IsHandledByGlobalHandler(ex))
                {
                    // the handler returned "true" means the exception is now "handled" and we should continue.
                }
            }
        }
    }
}