Tag Archives: race condition

SpotTheDefect[0].Answer[0]

Last week(ish) I posted a quick “Spot the Defect” game, where I asked you to:

  • Figure out what it would output
  • Find the bug(s)
  • Correct the bugs

So today is one of three posts with the answers – I’ll focus on the output, the bug and a trivial correction, with the next two posts diving deeper into other possible solutions.

Explosions, and not the good kind

So, most of you probably ran the code and got the output:

Hello, World!
Disposing

If you were lucky, or had a few things running in the background, you may also have seen:

Disposing

But, it was also possible to get:

Disposing
Hello, World!

Or, even a NullReferenceException!

How is this possible? Because of a very subtle race condition…

Scheduling Conflict

Most of the time, the application would have kicked off both threads, and these threads would run in their entirety without ever being preempted. Additionally, since we started Thread1 before Thread2, in all likelihood Windows would have scheduled and ran the threads in that order. However, it is entirely possible that Windows could decide to preempt either thread at any stage in our code, or execute them in any order. So, in order to see “Disposing” before “Hello, World!” we would need Thread2 to go first and then be preempted just after it wrote to the console to allow Thread1 to run in its entirety. The NullReferenceException is the opposite: If Thread1 is preempted just after it checks _disposed and enters its if-statement, then this allows Thread2 to “race in” and set _foo to null, causing Thread1 to hit a NullReferenceException when it resumes.

This kind of bug is especially difficult to diagnose because of how “tight” the timing is. You would see the exception intermittently at best (you can try this by wrapping it in a for-loop), and it would occur even less frequently if there was a debugger attached or if you added more diagnostics\tracing (simply because there becomes more “safe” lines of code to be preempted at, so the timing window becomes even tighter). And even if, somehow, you did manage to get a crash dump with the exception, you would need to dig through all of your code to try to find how you ended up crashing (which, if you’re not thinking about how threads can affect each other, is about as effective as bashing your head against a wall).

When in doubt, lock

At the beginning of this post, I described this as the “trivial” solution, but that doesn’t mean that you should discredit it – although locks seem like the most heavy handed approach, they are usually the safest as well. For those of you unfamiliar with C#’s lock keyword (or the Monitor Enter\Exit that it wraps), this is a language construct that acts similar to a critical section or a mutex, except that you do not create a synchronization primitive to manage the state of the lock, but rather you lock on an object (and the Monitor maintains the lock state). Take a minute to think about that: you must lock on an object – not a primitive, not a struct, and not null. This complicates matters for our code, since the only object we have is _foo, and we can’t guarantee that it isn’t null. We also can’t lock on the ‘this’ object because we are in a static method (not that you should ever lock on ‘this’ or any other object that is publically visible, as this can lead to unexpected deadlocks if other code decides to lock on your object or its properties\return values). The common solution to this problem is to add a dedicated ‘lock object’ that is only ever used for locking and is guaranteed to not be null.

Many developers shy away from locks as they are viewed as performance issues and the leading cause of hangs. While a lock is not free, you’d be surprised just how fast a lock is, especially when there is no contention on it. Deadlocks, however, always remain a problem – but, I can say from experience: it is much easier to reason about the ordering that locks are taken and released, than to consider and find the type of bug I’ve described above.

So, this “trivial” solution is to add a new “lock object”, and then to serialize access to _foo:

using System;
using System.Threading;

namespace SpotTheDefect1
{
    class Program
    {
        private static int _x = 0;
        private static Foo _foo = new Foo();
        private static bool _disposed = false;
        private static object _fooLockObject = new object();

        static void Main(string[] args)
        {
            Thread thread1 = new Thread(Thread1);
            Thread thread2 = new Thread(Thread2);

            thread1.Start();
            thread2.Start();

            thread1.Join();
            thread2.Join();
        }

        private static void Thread1()
        {
            if (!_disposed)
            {
                lock (_fooLockObject)
                {
                    if (_foo != null)
                    {
                        _foo.Bar();
                    }
                }
            }
        }

        private static void Thread2()
        {
            Console.WriteLine("Disposing");
            _disposed = true;
            lock (_fooLockObject)
            {
                _foo = null;
            }
        }

        private class Foo
        {
            public void Bar()
            {
                Console.WriteLine("Hello, World!");
            }
        }
    }
}
Advertisements
Tagged , , , ,