API review: During shutdown, revisit finalization and provide a way to clean up resources · Issue #16028 · dotnet/runtime (original) (raw)

Running finalizers on reachable objects during shutdown is currently unreliable. This is a proposal to fix that and provide a way to clean up resources on shutdown in a reliable way.

Issues observed on shutdown

Currently, a best-effort attempt is made to run finalizers for all finalizable objects during shutdown, including reachable objects. Running finalizers for reachable objects is not reliable, as the objects are in an undefined state.

Proposal

Behavioral change

public static void Main() { var obj = new MyFinalizable(); }

private class MyFinalizable { MyFinalizable() { Console.WriteLine("MyFinalizable"); } }

Previous output:
~MyFinalizable

Typical output with the proposal above (running the finalizer is not guaranteed, but may run if a GC is triggered):
(empty)

Proposed API

namespace System.Runtime.Loader { public abstract class AssemblyLoadContext { public event Action Unloading; } }

Example

public class Logger { private static readonly object s_lock = new object(); private static bool s_isClosed = false;

static Logger()
{
    var currentAssembly = typeof(Loader).GetTypeInfo().Assembly;
    AssemblyLoadContext.GetLoadContext(currentAssembly).Unloading += OnAssemblyLoadContextUnloading;

    // Create log file based on configuration
}

private static void OnAssemblyLoadContextUnloading(AssemblyLoadContext sender)
{
    // This may be called concurrently with WriteLine
    Close();
}

private static void Close()
{
    lock (s_lock)
    {
        if (s_isClosed)
            return;
        s_isClosed = true;

        // Write remaining in-memory log messages to log file and close log file
    }
}

public static void WriteLine(string message)
{
    lock (s_lock)
    {
        if (s_isClosed)
            return;

        // Save log message in memory, if buffer is full, write messages to log file
    }
}

}