Events get a little overhaul in C# 4, Part I: Locks

It’s been a long time since I’ve posted anything about the way the compiler generates field-like events, and I have some good news. We fixed them! Well, anyway, we changed them, and I believe that in C# 4, they are better than they used to be.

Read this old post to refresh yourself, if you care. The cause for alarm, through C# 3, was that when you declared events, the compiler made event accessors that took a lock on the “this” sync block, or worse. This meant that calls to the accessors were safe to make on multiple threads, but it is bad design that could cause deadlocks, and made it hard to program lock-free using events.

Let’s have a look at what changed. Suppose we have the following simple event declaration:

 class Control
{
    public event EventHandler OnClick;
}

In C# 3, we generated event handlers that looked something like this

 class Control
{
    private EventHandler __OnClick; // This is the backing field
    public event EventHandler OnClick
    {
        add { lock (this) { __OnClick = __OnClick + value; } }
        remove { lock (this) { __OnClick = __OnClick - value; } }
    }
}

That old post explains it in some detail, but the overall badness comes from the lock. You should never lock “this.” So, the question becomes, what can we do to improve the codegen here?

Well, we really have to get rid of that lock. There are a variety of options available to us. We can just delete the lock entirely and leave the delegate operations naked. Or we can replace it with a lock on something safe (say, a field we’d create just for this purpose in your class). Or we can introduce some other synchronization mechanism.

If we got rid of the lock, though, that would really be a problem for people who need this synchronization. After all, it was added for a reason, and surely people are relying on it. As for the second option, well, that would bloat your instances all by a reference, which you might or might not care about, but someone does. After all, the compiler is going to be doing this for everyone who uses field-like events.

So, we went with option three. Now, the compiler generates something like this:

 class Control
{
    private EventHandler __OnClick; // This is the backing field
    public event EventHandler OnClick
    {
        add { /* lock-free synchronization code that updates __OnClick */ }
        remove { /* lock-free synchronization code that updates __OnClick */ }
    }
}

Well, that’s not so instructive, but I don’t want to sidetrack you with the details of exactly what the code looks like (it’s a compare-and-swap). It’s interesting, but it’s not the point (go look at your classes with ildasm and you can see it; we’ll talk later). With this new code, in C# 4, your event accessors will have the following characteristics:

  1. The accessors do not use the Monitor class at all, and can therefore be used in an environment that is hostile to locks, such as SQLCLR.
  2. For each field-like event, all add and remove calls will be effectively serial, regardless of the threads the calls take place on. Adds and removes on separate events can happen simultaneously, but that’s no problem.
  3. This synchronization works for instance events and static events, and it works in reference types (classes) as well as value types (structs). This is an improvement over the previous model that did not support value types.

These are really good things. But there is some cost; unfortunately it’s not all Tootsie Rolls and unicorns. I’ll follow up with more about the downsides in the next few posts, as well as other compiler changes that mitigate them.