Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get notified before static variables are finalized

When can i cleanup objects stored in static variables in C#?

I have a static variable that is lazily initialized:

public class Sqm
{
    private static Lazy<Sqm> _default = new Lazy<Sqm>();

    public static Sqm Default { get { return _default.Value; } }
}

Note: That i've just changed Foo to be a static class. It doesn't change the question in any way if Foo is static or not. But some people are convinced that there is no way that an instance of Sqm could be constructed without first constructing an instance of Foo. Even if i did create a Foo object; even if i created 100 of them, it wouldn't help me solve the problem (of when to "cleanup" a static member).

Sample usage

Foo.Default.TimerStart("SaveQuestion");
//...snip...
Foo.Default.TimerStop("SaveQuestion");

Now, my Sqm class implements a method that must be called when the object is no longer needed, and needs to clean itself up (save state to filing system, release locks, etc). This method must be called before the garbage collectors runs (i.e. before my object's finalizer is called):

public class Sqm
{
   var values = new List<String>();         
   Boolean shutdown = false;

   protected void Cleanup(ICollection stuff)
   {
      WebRequest http = new HttpWebRequest();
      http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
      http.PostBody = stuff;
      http.Send();
   }

   public void Shutdown()
   { 
      if (!alreadyShutdown)
      {
         Cleanup(values);
         alreadyShutdown = true;
      }
   }
}

When, and where, can i call my Shutdown() method?

Note: I don't want the developer who uses the Sqm class to have to worry about calling Shutdown. That's not his job. In other language environments he would not have to.

The Lazy<T> class does not seem to call Dispose on the Value it lazily owns. So i cannot hook the IDisposable pattern - and use that as the time to call Shutdown. I need to call Shutdown myself.

But when?

It's a static variable, it exists once for the life of the application/domain/appdomain/apartment.

Yes, the finalizer is the wrong time

Some people do understand, and some people don't, that trying to upload my data during a finalizer is wrong.

///WRONG: Don't do this!
~Sqm
{
   Shutdown(_values); //<-- BAD! _values might already have been finalized by the GC!
}   

Why is it wrong? Because values might not be there anymore. You don't control what objects are finalized in what order. It is entirely possible that values was finalized before the containing Sqm.

What about dispose?

The IDisposable interface, and the Dispose() method is a convention. There is nothing that dictates that if my object implements a Dispose() method that it will ever be called. In fact, i could go ahead and implement it:

public class Sqm : IDisposable
{
   var values = new List<String>();         
   Boolean alreadyDiposed = false;

   protected void Cleanup(ICollection stuff)
   {
      WebRequest http = new HttpWebRequest();
      http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
      http.PostBody = stuff;
      http.Send();
   }

   public void Dispose()
   { 
      if (!alreadyDiposed)
      {
         Cleanup(values);
         alreadyDiposed = true;
      }
   }
}

To the person actually reading the question, you might notice that i didn't actually change anything. The only thing i did was to change the name of a method from Shutdown to Dispose. The Dispose pattern is simply a convention. i still have the problem: when can i call Dispose?

Well you should call dispose from your finalizer

Calling Dispose from my finalizer is just as incorrect as calling Shutdown from my finalizer (they are identically wrong):

public class Sqm : IDisposable
{
   var values = new List<String>();         
   Boolean alreadyDiposed = false;

   protected void Cleanup(ICollection stuff)
   {
      WebRequest http = new HttpWebRequest();
      http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
      http.PostBody = stuff;
      http.Send();
   }

   public void Dispose()
   { 
      if (!alreadyDiposed)
      {
         Cleanup(_values); // <--BUG: _values might already have been finalized by the GC!
         alreadyDiposed = true;
      }
   }

   ~Sqm
   {
      Dispose();
   }
}

Because, again, values might not be there anymore. For completeness, we can return to the full original correct code:

public class Sqm : IDisposable
{
   var values = new List<String>();         
   Boolean alreadyDiposed = false;

   protected void Cleanup(ICollection stuff)
   {
      WebRequest http = new HttpWebRequest();
      http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
      http.PostBody = stuff;
      http.Send();
   }

   protected void Dispose(Boolean itIsSafeToAlsoAccessManagedResources)
   { 
      if (!alreadyDiposed)
      {
         if (itIsSafeToAlsoAccessManagedResources)
            Cleanup(values);
         alreadyDiposed = true;
      }
   }

   public void Dispose()
   {
      this.Dispose(true);
      GC.SuppressFinalize(this);
   }

   ~Sqm
   {
      Dispose(false); //false ==> it is not safe to access values
   }
}

i've come full circle. i have an object which i need to "cleanup" before the application domain shuts down. Something inside my object needs to be notified when it can call Cleanup.

Make the developer call it

No.

i'm migrating existing concepts from another language into C#. If a developer happens to use the global singleton instance:

Foo.Sqm.TimerStart();

then the Sqm class is lazy initialized. In a (native) application, the reference to the object is held. During (native) application shutdown, the variable holding the interface pointer is set to null, and the singleton object's destructor is called, and it can clean itself up.

Nobody should ever have to call anything. Not Cleanup, not Shutdown, not Dispose. Shutdown should happen automatically by the infrastructure.

What is the C# equivalent of i see myself going away, clean myself up?

It's complicated by the fact that if you let the garbage collector collect the object: it's too late. The internal state objects i want to persist are likely already finalized.

It would be easy if from ASP.net

If i could guarantee that my class were being used from ASP.net, i could ask the HostingEnvironment to notify before the domain shuts down by registering my object with it:

System.Web.Hosting.HostingEnvironment.RegisterObject(this);

And implement the the Stop method:

public class Sqm : IDisposable, IRegisteredObject
{
   var values = new List<String>();         
   Boolean alreadyDiposed = false;

   protected void Cleanup(ICollection stuff)
   {
      WebRequest http = new HttpWebRequest();
      http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
      http.PostBody = stuff;
      http.Send();
   }

   protected void Dispose(Boolean itIsSafeToAlsoAccessManagedResources)
   { 
      if (!alreadyDiposed)
      {
         if (itIsSafeToAlsoAccessManagedResources)
            Cleanup(values);
         alreadyDiposed = true;
      }
   }

   public void Dispose()
   {
      this.Dispose(true);
      GC.SuppressFinalize(this);
   }

   Sqm
   {
      //Register ourself with the ASP.net hosting environment,
      //so we can be notified with the application is shutting down
      HostingEnvironment.RegisterObject(this); //asp.net will call Stop() when it's time to cleanup
   }

   ~Sqm
   {
      Dispose(false); //false ==> it is not safe to access values
   }

   // IRegisteredObject
   protected void Stop(Boolean immediate)
   {
      if (immediate) 
      {
         //i took too long to shut down; the rug is being pulled out from under me.
         //i had my chance. Oh well.
         return;
      }

      Cleanup(); //or Dispose(), both good
   }
}

Except my class doesn't know if i'll be called from ASP.net, or from WinForms, or from WPF, or a console application, or shell extension.

Edit: People seem to be confused by what the IDisposable pattern exists for. Removed references to Dispose in order to remove the confusion.

Edit 2: People seem to be demanding full, detailed, example code before they will answer the question. Personally i think the question already contains too much example code, as it doesn't serve to help ask the question.

And now that i've added sooo much code, the question has been lost. People refuse to answer a question until the question has been justified. Now that it's been justified, nobody will read it.

It's like diagnostics

It's like the System.Diagnostics.Trace class. People call it when they want:

Trace.WriteLine("Column sort: {0} ms", sortTimeInMs);

and never have to think of it again.

And then desperation sets in

i was even desperate enough, that i considered hiding my object behind an COM IUnknown interface, which is reference counted

public class Sqm : IUnknown
{
   IUnknown _default = new Lazy<Sqm>();
}

And then hopefully i could trick the CLR into decrementing the reference count on my interface. When my reference count becomes zero, i know everything is shutting down.

The downside of that is that i cannot make it work.

like image 718
Ian Boyd Avatar asked Aug 02 '13 15:08

Ian Boyd


People also ask

When the static variables will be created?

Static variables are initialized only once , at the start of the execution. These variables will be initialized first, before the initialization of any instance variables. A single copy to be shared by all instances of the class. A static variable can be accessed directly by the class name and doesn't need any object.

Do static variables remain even after closing the program?

A static variable continues to exist and retains its most recent value. The next time your code calls the procedure, the variable is not reinitialized, and it still holds the latest value that you assigned to it. A static variable continues to exist for the lifetime of the class or module that it is defined in.

Are static variables initialized at compile time?

Global and static variables are initialized to their default values because it is in the C or C++ standards and it is free to assign a value by zero at compile time. Both static and global variable behave same to the generated object code.

Do static variables need to be initialized?

Static variables are initialized only once. Compiler persist the variable till the end of the program. Static variable can be defined inside or outside the function. They are local to the block.


3 Answers

There are two problems here:

  • You are insistent that the List<string> might have been finalized. List<string> doesn't have a finalizer, and it won't have been garbage collected yet (because you have a reference to it). (Those are different operations.) Your SQL finalizer will still see valid data. So actually, a finalizer might be okay - although by the time the finalizer runs some other resources that you require may have gone away - and the finalizer may not even end up being called. So I think this is simultaneously more feasible than you expect - and a worse idea in general.

  • You are insistent that you don't want to make this deterministic by putting it in the control of the developer, whether that's using IDisposable or not. This is simply fighting against what .NET provides. The garbage collector is meant to be for memory resources; any non-memory resources which require deterministic cleanup (including flushing etc) should be cleaned up explicitly. You can use a finalizer as a last "best effort" clean-up, but it should not be used in the way you're trying to use it.

There are some approaches that you could use to try to work around this, such as using a "canary" object with a reference to the "real" object: keep a strong reference to the object you're interested elsewhere, and have a finalizer just in the canary object, so that the only thing to be finalized is the canary object - which then triggers the appropriate flushing and removes the last strong reference, leaving the real object eligible for GC - but it's still fundamentally a bad idea, and with static variables in the mix it becomes even worse.

Likewise you can use the AppDomain.DomainUnload event - but again, I wouldn't. By the time the domain is being unloaded, I would be worried about the state of the rest of the objects - and it doesn't get called for the default domain.

Basically, I think you should change your design. We don't really know the background of the API you're trying to design, but the way you're going at the moment just won't work. I would try to avoid the static variable, personally - at least for anything which is important in terms of timing. There could still be a single object behind the scenes for coordination, but exposing that in your API feels like a mistake to me. However much you protest about other languages and other platforms, if you're working in .NET you need to accept it is what it is. Fighting against the system is not going to help you in the long term.

The earlier you come to the conclusion that you need to change your API design, the more time you have to think about what that new API should look like.

like image 118
Jon Skeet Avatar answered Oct 16 '22 11:10

Jon Skeet


There's a ProcessExit event on the AppDomain you could try to hook, though I don't know much more about it, and it has a default time limit of 2 seconds.

Something like this (if it's suitable for you);

class SQM
{

    static Lazy<SQM> _Instance = new Lazy<SQM>( CreateInstance );

    private static SQM CreateInstance()
    {
        AppDomain.CurrentDomain.ProcessExit += new EventHandler( Cleanup );
        return new SQM();
    }

    private static Cleanup()
    {
        ...
    }

}
like image 26
Gareth Wilson Avatar answered Oct 16 '22 11:10

Gareth Wilson


i had asked this question four times, if four different ways. Phrasing each one slightly different; trying to solve the problem from a different direction. Eventually it was M.A. Hanin that pointed me at this question that solved the problem.

The issue is that there is no single way to know when the domain is shutting down. The best you can do is try to catch various kinds of events that cover you 100% (rounded to the nearest percentage) of the time.

If the code is in some domain besides the default, then use the DomainUnload event. Unfortunately the default AppDomain doesn't raise a DomainUnload event. So then we catch ProcessExit:

class InternalSqm 
{
   //constructor
   public InternalSqm ()
   {
      //...

      //Catch domain shutdown (Hack: frantically look for things we can catch)
      if (AppDomain.CurrentDomain.IsDefaultAppDomain())
         AppDomain.CurrentDomain.ProcessExit += MyTerminationHandler;
      else
         AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler;
   }

   private void MyTerminationHandler(object sender, EventArgs e)
   {
      //The domain is dying. Serialize out our values
      this.Dispose();
   }

   ...
}

This has been tested inside a "web-site" an a "WinForms" application.

The more complete code, showing an implementation of IDisposable:

class InternalSqm : IDisposable
{
   private Boolean _disposed = false;

   //constructor
   public InternalSqm()
   {
      //...

      //Catch domain shutdown (Hack: frantically look for things we can catch)
      if (AppDomain.CurrentDomain.IsDefaultAppDomain())
         AppDomain.CurrentDomain.ProcessExit += MyTerminationHandler;
      else
         AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler;
   }

   private void MyTerminationHandler(object sender, EventArgs e)
   {
      //The domain is dying. Serialize out our values
      this.Dispose();
   }

.

   /// <summary>
   /// Finalizer (Finalizer uses the C++ destructor syntax)
   /// </summary>
   ~InternalSqm()
   {
      Dispose(false); //False: it's not safe to access managed members
   }

   public void Dispose()
   {
      this.Dispose(true); //True; it is safe to access managed members
      GC.SuppressFinalize(this); //Garbage collector doesn't need to bother to call finalize later
   }

   protected virtual void Dispose(Boolean safeToAccessManagedResources)
   {
      if (_disposed)
         return; //be resilient to double calls to Dispose

      try
      {
         if (safeToAccessManagedResources)
         {
            // Free other state (managed objects).                   
            this.CloseSession(); //save internal stuff to persistent storage
         }
         // Free your own state (unmanaged objects).
         // Set large fields to null. Etc.
      }
      finally
      {
         _disposed = true;
      }
   }
}

Sample usage

From a library that does image processing:

public static class GraphicsLibrary
{
    public Image RotateImage(Image image, Double angleInDegrees)
    {
       Sqm.TimerStart("GraphicaLibrary.RotateImage");
       ...
       Sqm.TimerStop("GraphicaLibrary.RotateImage");
    }
}

From a helper class that can execute a query

public static class DataHelper
{
    public IDataReader ExecuteQuery(IDbConnection conn, String sql)
    {
       Sqm.TimerStart("DataHelper_ExecuteQuery");
       ...
       Sqm.TimerStop("DataHelper_ExecuteQuery");
    }
}

For WinForms themed drawing

public static class ThemeLib
{
   public void DrawButton(Graphics g, Rectangle r, String text)
   {
      Sqm.AddToAverage("ThemeLib/DrawButton/TextLength", text.Length);
   }
}

In a web-site:

private void GetUser(HttpSessionState session)
{
   LoginUser user = (LoginUser)session["currentUser"];

   if (user != null)
      Sqm.Increment("GetUser_UserAlreadyFoundInSession", 1);

   ...
}

In an extension method

/// <summary>
/// Convert the guid to a quoted string
/// </summary>
/// <param name="source">A Guid to convert to a quoted string</param>
/// <returns></returns>
public static string ToQuotedStr(this Guid source)
{
   String s = "'" + source.ToString("B") + "'"; //B=braces format "{6CC82DE0-F45D-4ED1-8FAB-5C23DE0FF64C}"

   //Record how often we dealt with each type of UUID
   Sqm.Increment("String.ToQuotedStr_UUIDType_"+s[16], 1);

   return s;
}

Note: Any code is released into the public domain. No attribution required.

like image 25
Ian Boyd Avatar answered Oct 16 '22 09:10

Ian Boyd