As the other answers and comments have said you need to implement some form of reference counting. Here is my attempt at doing it (I was working on this when you posted your answer), It still uses a singleton Engine
(there is no requirement now to do so, you could make it a static class with minimal changes), however callers need to call AddRefrence()
and ReleaseRefrence()
to let the engine know if it needs to setup or tear down the API when the count hits 1 or 0 respectively.
Module
supports releasing it's reference on calling Dispose()
or when the class is finalized.
using System.Threading;
namespace FinalizerOrder
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Engine
{
private Engine()
{
//ActivateEngine() is no longer called here.
}
private readonly static Engine _singleton = new Engine(); //Now that the constructor is empty we can initialize immediately.
private readonly static object _syncLock = new object();
private static volatile int _counter = 0;
public static Engine Singleton
{
get { return _singleton; }
}
public void AddRefrence()
{
lock (_syncLock)
{
_counter++;
if (_counter < 0)
throw new InvalidOperationException("ReleaseRefrence() was called more times than AddRefrence()");
if(_counter == 1)
Debug.WriteLine("ActivateEngine() ...");
}
}
public void ReleaseRefrence()
{
lock (_syncLock)
{
_counter--;
if (_counter < 0)
throw new InvalidOperationException("ReleaseRefrence() was called more times than AddRefrence()");
if (_counter == 0)
{
Debug.WriteLine("TerminateEngine() ...");
}
}
}
}
class Module : IDisposable
{
public Module()
{
Engine.Singleton.AddRefrence();
_id = _counter++;
Debug.WriteLine("CreateModule() ==> {0} ...", _id);
}
private readonly int _id;
private static int _counter;
~Module()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed = false;
protected void Dispose(bool disposing)
{
if(_disposed)
return;
_disposed = true;
if (disposing)
{
//Nothing to do here, no IDisposeable objects.
}
Debug.WriteLine("DestroyModule({0}) ...", _id);
Engine.Singleton.ReleaseRefrence();
}
}
internal class Program
{
private static void Main()
{
Test();
GC.Collect(3, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Test();
}
private static void Test()
{
var module1 = new Module();
var module2 = new Module();
GC.KeepAlive(module2);
GC.KeepAlive(module1);
}
}
}