tags:

views:

524

answers:

3

Dupe: return statement in a lock procedure: inside or outside

The title is a little misleading. I know that you can do it, but I'm wondering about the performance implications.

consider these two blocks of code. (no error handling)

This block has the return outside of the lock

 public DownloadFile Dequeue()
 {
     DownloadFile toReturn = null;
     lock (QueueModifierLockObject)
     {
         toReturn = queue[0];
         queue.RemoveAt(0);
     }
     return toReturn;
 }

This block has the return statement within the lock

 public DownloadFile Dequeue()
 {
     lock (QueueModifierLockObject)
     {
         DownloadFile toReturn = queue[0];
         queue.RemoveAt(0);

         return toReturn;
     }
 }

Is there any difference in the code? I understand that the performance differences (if any) would be minimal, but I am specifically wondering if there would be a difference in the order that the lock gets release.

+1  A: 

I believe the IL would be identical... I'd have to test it to be fer sure, but the lock statement generates a try finally in the IL, and the return would trigger the finally (with the release) BEFORE the stack frame closes and returns to the caller anyway, so...

Charles Bretana
A: 

Yes, but why not use Dequeue?

Remember lock is simply shorthand for essentially something along the lines of:

        try
        {
            Monitor.Enter(QueueModifierLockObject);

            DownloadFile toReturn = queue.Dequeue();         

            return toReturn;
        }
        finally
        {
            Monitor.Exit(QueueModifierLockObject);
        }
Philip
+10  A: 

The C# compiler will move the return statement outside of the try/finally that is created for the lock statement. Both of your examples are identical in terms of the IL that the compiler will emit for them.

Here is a simple example proving that:

class Example
{
    static Object obj = new Object();

    static int Foo()
    {
     lock (obj)
     {
      Console.WriteLine("Foo");
      return 1;
     }
    }

    static int Bar()
    {
     lock (obj)
     {
      Console.WriteLine("Bar");
     }
     return 2;
    }
}

The code above gets compiled to the following:

internal class Example
{
        private static object obj;

        static Example()
        {
                obj = new object();
                return;
        }

        public Example()
        {
                base..ctor();
                return;
        }

        private static int Bar()
        {
                int CS$1$0000;
                object CS$2$0001;
                Monitor.Enter(CS$2$0001 = obj);
        Label_000E:
                try
                {
                        Console.WriteLine("Bar");
                        goto Label_0025;
                }
                finally
                {
                Label_001D:
                        Monitor.Exit(CS$2$0001);
                }
        Label_0025:
                CS$1$0000 = 2;
        Label_002A:
                return CS$1$0000;
        }

        private static int Foo()
        {
                int CS$1$0000;
                object CS$2$0001;
                Monitor.Enter(CS$2$0001 = obj);
        Label_000E:
                try
                {
                        Console.WriteLine("Foo");
                        CS$1$0000 = 1;
                        goto Label_0026;
                }
                finally
                {
                Label_001E:
                        Monitor.Exit(CS$2$0001);
                }
        Label_0026:
                return CS$1$0000;
        }
}

As you can see, the compiler has taken the libery of moving the return statement in Foo outside of the try/finally.

Andrew Hare
Indeed. And if you go look at the generated IL, you'll see why we do that. You can only leave a protected region with a special "leave" instruction which knows how to clean up the exception handling goo (or, of course, by throwing an exception). You cannot do ordinary branches or returns out of a protected region. Therefore we have to generate leaves from the protected region to a return generated outside the region.
Eric Lippert