hash_bucket()

CallbackOnCollectedDelegate

Posted on: September 26, 2006

…seems to be one of the keywords that a lot of people find this Blog on, so I figured I’d take a few minutes to explain the problem in more detail. Most of the below information can be found on MSDN, but maybe I can put it in a more concise and easy to grasp language.(?)

The problem of encountering a collected delegate really needs to be understood in the context of the workings of the Garbage Collector (GC) that is responsible for reclaiming and tidying up the memory no longer in use by managed code. To put it simple, when no more references exist to a managed piece of memory, that piece or area can safely be marked as available and overwritten with new data. How does this happen in practice? I’ll start by explaining some basics. Let’s look at some code:

class Program
{
  class Point
  {
       public int X;
       public int Y;

       public Point(int x, int y)
      {
          this.X = x;
          this.Y = y;
      }
  }

  static void Main(string[] args)
  {
        Point p = new Point(0, 0);
  }
}

In this very simple piece of code we have our standard console application starting point, the Program class with its Main function, an internal class to represent a Point and an instance of this Point class within the Main function.

No sweat so far right, we all know that when the Point is declared and instantiated a reference that points to a memory area within the managed heap is created on the stack. Thus what p in this case really is is a Pointer on the stack pointing to a location whitin the managed heap where our real data lives.

Now for the execution lifetime of this very short program the piece of memory on the managed heap that contains our Point data will remain “tagged” as being “in use” by the program, the reason for this is that p never goes out of scope. That is, a reference to this memory will always exist. When the GC makes its irregular sweep through our managed heap memory it will notice this and thus it wont touch this piece of memory but leave it intact.

(Actually it might move it around in memory to speed things up for our program. Should this happen our pointer p will be updated (by the GC) to point to the new location.)

Later in our program, if another piece of code tries to read the data that p points to it will find it (intact) by looking at the location that p points at. Still no sweat? Good.

Let’s look at another example:

  class Program
  {
    class Point
    {
      public int X;
      public int Y;

      public Point(int x, int y)
      {
         this.X = x;
         this.Y = y;
      }
    }

    static void Main(string[] args)
    {
        CreateAPoint();
    }

    private void CreateAPoint()
    {
        Point p2 = new Point(0, 0);
    }
  }

In this sample our program has been extended with a fancy method, CreateAPoint, that creates a Point. This method is called in our Main method and it does nothing but declare and instantiate a Point called p2. After this the method exits.

All the stuff said above still goes for this new Point p2, but the interesting thing happens when we exit the method. As I said above, the memory pointed at by our previous Point p will remain tagged for the execution lifetime of our program, the reason being that p never went out of scope. In our new piece of code the variable p2 actually looses its scope as soon as the method exits. Thus, after CreateAPoint has exited, the next time the GC sweeps the memory, it will find an area occupied by the data declared by p2, but no active references to it. In other words, there are no variables pointing at this piece of data. Thus, from the GCs point of view it can be safely tagged as available to be overridden by new data, or in GC terms, it will be Collected.

Actually all the Reference Types:

  • Classes
  • Boxed Value Types
  • Arrays
  • Delegates

behave in the same way, and as you can see from the list this includes our main character for this post, the Delegate.

So what is a Delegate. Well if you ask a C++ programmer she will tell you that it is a function pointer, or sometimes a callback function, and while they share many similarities, there are also some important differences (the delegate being type-safe for example). In C++ a function pointer is used to pass a method as a parameter to another method. A very useful feature indeed. In C# one of the most common usages of delegates is to declare Events and Callbacks that enable various objects in our code to report state and pass information between them.

The above can be seen in the following sample code:

 delegate void SomethingHappened(string What);

class Program
{
    public static event SomethingHappened SomethingHappenedInstance;

    static void Main(string[] args)
    {
        SomethingHappenedInstance += new SomethingHappened(Program_SomethingHappenedInstance);
        SomethingHappenedInstance(“Roger Wilco has left the Building”);
    }

    static void Program_SomethingHappenedInstance(string What)
    {
         Console.WriteLine(What);
     }
  }

This piece of code declares a delegate called SomethingHappened that takes a string as its parameter. What it does with this string is entirely up to our own implementation of this delegate as we will soon see. Within our Program class we then declare an event that appears to be of type SomethingHappened. We call it SomethingHappenedInstance. Next in our code is where the magic happens. 

I said earlier that a delegate can be thought of as a pointer to a method or a callback method. To explain this in a simple way we think of it as containing a list of methods that matches its signature (in this case it takes a string and returns nothing) and invokes these methods one by one when it is “activated”. In our case its list contains only one method (called Program_SomethingHappenedInstance) that we added in the line

SomethingHappenedInstance += new SomethingHappened(Program_SomethingHappenedInstance);

When we invoke our SomethingHappenedInstance:

SomethingHappenedInstance(“Roger Willco has left the Building”);

It will call all the methods in its “list” and pass the string we sent in to each and every one of them.

Good so now we have a basic idea of what a delegate is. Remember though that since a delegate instance is a reference type it obeys the same laws about scope and garbage collection as the first little Point class I showed.

On to the juicier parts. What about those pesky collected delegates? Well by now you should be able to understand the problem of a CallbackOnCollectedDelegate basically by looking at the name itself. Or at least to some extent. Actually there is another tiny matter that also needs to be understood in order to come to grips with the CollectedDelegate problem. Basically the problem will only arise when the callback begins its life in the world of managed code, and then is passed through interop to unmanaged code. The reason for this is that once the address that the method pointer points at has been sent of to Unamanaged Land, there is no way for the GC to know that there still exists a reference to this address. This is of course because whatever data is handled in unmanaged code lives outside the managed heap, and thus out of sight from the GC. With this said lets move on.

Since a delegate is a reference type, the memory it points to at the heap will be collected whenever it goes out of scope, that is, the GC will mark the memory region it pointed to as “empty” (that is available for use), and the callback that tries to call the delegate will call into open space. If this was tricky and abstract lets look at a real world example.

Say I send you a postcard that says “Please send this back to the same address it came from as soon as you get it!”. Then after sending it off I move to a new address. You send the card back but when it arrives where I used to live I am nowhere to be found. This will make a lot of people sad. I will be sad because I never got your card, the post man will be sad and confused because he can’t do his job and you will be sad because eventually the card will return to you with an error message attached to it.

So, let’s look an example where the CallbackOnCollectedDelegate will NOT happen:

 delegate void SomethingHappened(string What);

class Program
  {
  public static event SomethingHappened SomethingHappenedInstance;

static void Main(string[] args)
  {
  SomethingHappenedInstance += new SomethingHappened(Program_SomethingHappenedInstance);
  DoSomething(SomethingHappenedInstance, “Print a line”);
  Console.Read();
  }

static void Program_SomethingHappenedInstance(string What)
  {
  Console.WriteLine(What);
  }

static void DoSomething(SomethingHappened callback, string whatdoto)
  {
  Console.WriteLine(“hej”);
  callback(“Done”);
  }
  }

In this case we have a function that will DoSomething. It takes a callback that it can invoke to tell us when it is done and a string that defines an action. In this case, since our callback, which is a delegate, was defined in the class scope of our Program class it will remain in-use, untouched by the GC for the execution lifetime of our program. This means that whenever the DoSomething invokes the callback it will always find its way back to the correct method in memory that it points to.

However, if the method that we send the callback to lives in unmanaged code, and the location of the method that the callback points to in memory gets Collected by the GC, then as the unmanaged code tries to invoke the callback it will call into nothing. Giving rise to a CallbackOnCollectedDelegate.

Did that make sense?

Here is a link to an article on MSDN that also explains the whole issue:

http://msdn2.microsoft.com/en-us/library/43yky316.aspx

Advertisements

3 Responses to "CallbackOnCollectedDelegate"

Well, what should I do if I’m passing a callback reference to unmanaged code?

Make sure the reference does not go out of scope for the execution lifetime of the program that passed the reference. :)

I don’t understand that. Isn’t a method code? Why does code being move by teh GC? Don’t all the class instantiations use their own data but use the same code at the same location?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

.

This blog has no clear focus. It has a focus though, it's just not very clear at the moment...

Dev Env.

Visual Studio 2008 Prof / NUnit / Gallio / csUnit / STools (ExactMagic) / doxygen / dxCore / TypeMock / TestDriven.net / SequenceViz / CLRProfiler / Snoop / Reflector / Mole / FxCop / Subversion / TortoiseSVN / SlikSVN / CruiseControl.net / msbuild / nant

Blog Stats

  • 81,467 hits