2

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
  }
}
Jeff
  • 2,491
  • 2
  • 20
  • 35

2 Answers2

0

I'm jumping in feet first here as there are a lot of unknowns about you particular code base, but I'm wondering is there is any mileage in a session based approach? You could have a (thread safe) session factory singleton that is responsible for ensuring only one environment is initialised and that environment is disposed appropriately by binding it to events on the ASP.NET AppDomain and/or similar. You would need to bake this session model into your API so that all client first established a session before making any calls. Apologies for the vagueness of this answer. If you can provide some example code perhaps I could give a more specific/detail answer.

Myles McDonnell
  • 11,575
  • 14
  • 56
  • 96
  • It's a reasonable suggestion. I guess we're trying to avoid sessions in an attempt to make every call as stateless as possible. I think it's fairly likely we would end up with a similar problem when sessions are unexpectedly terminated. I apologize for not posting code, but it's a tricky problem to post code for... – Jeff Feb 13 '12 at 15:47
  • How about using AOP to hide the session management from the API, thus making it appear stateless? You could intercept all public API calls to ensure the environment is initialised before proceeding with the call. Again, bind the lifetime of the process to that of the client AppDomain. I'm grabbing at straws here, but that is my two pence worth. – Myles McDonnell Feb 13 '12 at 16:10
0

One approach you might want to consider is to create an isolated AppDomain for your unmanaged component. In this way it won't be orphaned when an IIS-hosted AppDomain is recycled.

Joe
  • 114,633
  • 27
  • 187
  • 321
  • Interesting...why wouldn't IIS also recycle this domain? – Jeff Feb 13 '12 at 16:57
  • My understanding is that you can recycle an ASP.NET Application (which is an AppDomain), e.g. by "touching" web.config or a file in the bin folder. If this happens, I believe other AppDomains in the same process (e.g. other ASP.NET applications) will keep running. On the other hand, if an application pool (IIS6 and later) is recycled, the worker process will be shut down and restarted. The problem you describe suggests that the process isn't being recycled, only the AppDomain. – Joe Feb 13 '12 at 21:20