We've run into a resource management problem that we've been struggling with for several weeks now and while we finally have a solution, it still seems weird to me.
We have a significant amount of interop code we've developed against a legacy system, which exposes a C API. One of the many peculiarities of this system is that (for reasons unknown), the "environment", which appears to be process-scoped must be initialized prior to the API being consumed. However, it can only be initialized once and must be "shutdown" once you're finished with it.
We were originally using a singleton pattern to accomplish this but as we're consuming this system inside an IIS hosted web service, our AppDomain will occasionally be recycled, leading to "orphaned" environments that leak memory. Since finalization and (apparently) even IIS-recycling is non-deterministic and hard to detect in all cases, we've switched to a disposal+ref counting pattern that seems to work well. However, doing reference counting manually feels weird and I'm sure there's a better approach.
Any thoughts on managing a static global disposable resource in an environment like this?
Here's the rough structure of the environment management:
public class FooEnvironment : IDisposable
{
private bool _disposed;
private static volatile int _referenceCount;
private static readonly object InitializationLock = new object();
public FooEnvironment()
{
lock(InitilizationLock)
{
if(_referenceCount == 0)
{
SafeNativeMethods.InitFoo();
_referenceCount++;
}
}
}
public void Dispose()
{
if(_disposed)
return;
lock(InitilizationLock)
{
_referenceCount--;
if(_referenceCount == 0)
{
SafeNativeMethods.TermFoo();
}
}
_disposed = true;
}
}
public class FooItem
{
public void DoSomething()
{
using(new FooEnvironment())
{
// environment is now initialized (count == 1)
NativeMethods.DoSomething();
// superfluous here but for our purposes...
using(new FooEnvironment())
{
// environment is initialized (count == 2)
NativeMethods.DoSomethingElse();
}
// environment is initialized (count == 1)
}
// environment is unloaded
}
}