LA.NET [EN]

Jul 06

In the last post, I’ve showed you some code I’ve written in  the past and asked if there was anything wrong with it. Here’s the code again:

class Lazy {
  private SomeObject _object;
  private Object _locker = new Object();
  public SomeObject SomeObject {
    get {
      if (_object == null) {
        lock (_locker) {
          if (_object == null) {
            _object = new SomeObject();
          }
        }
      }
      return _object;
    }
  }
}


Here’s main idea (for the double-checked locked pattern): you start by checking the instance against null. If it is null, then you acquire the lock and test again. We’re testing it again because someone might have already initialized the instance in the time that passed since the initial test and the lock acquisition. Ok, so is there anything wrong with this?



To answer this question correctly, we need to go back to the memory model supported by the CLR. As we’ve seen, the CLR won’t allow store-store reorderings (meaning that all the other types are allowed). If we assume that SomeObject has fields (and this is really a valid assumption), then they will be initialized during construction. So, if we’re using the CLR, everything should be ok because store-store aren’t allowed.



However, the CLR allows load-load reorderings, meaning that the load of the instance can be moved after the load of its fields, meaning that we could get into trouble. And what happens if we’re writing code that should be run against another ECMA CLI implementation? For instance,say we want to write code that will also run in Mono (I don’t really know Mono,so I don’t know if it follows the CLR 2.0 tighter rules). In this case, store-store reordering are possible and our code might break if the store of the _object instance occurs before the store of its fields. In this case, the testing condition will be null (_object won’t be null) but its fields are because they haven’t been written to yet. Solving this is as simple as adding volatile (recall that volatile allows only store-load reordering!). Here’s the code again:



class Lazy {
  private volatile SomeObject _object;
  private Object _locker = new Object();
  public SomeObject SomeObject {
    get {
      if (_object == null) {
        lock (_locker) {
          if (_object == null) {
            _object = new SomeObject();
          }
        }
      }
      return _object;
    }
  }
}


And that’s it. Adding the volatile keyword solves all the problems mentioned before. Keep tuned for more on multithreading.

3 comments so far

  1. Timothy Fries
    5:47 pm - 7-6-2009

    I thought Monitor.Enter and Monitor.Exit provided memory fences implicitly. Is that not the case?

  2. Luis Abreu
    6:23 pm - 7-6-2009

    Yes, they do, but don”t forget that load-load are allowed here (ex.: fields of the object loaded before the instance) and some processors do use it. Applying the volatile word to that instance doesn”t let that happen. You don”t need to apply it to the instance type fields though. Joe Duffy has a nice post on it here http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>