« October 2008 | Main | December 2008 »

November 20, 2008

Events and Threads (Part 4)

A common pattern for raising an event in C# is the following code:

EventHandler handler = MyEvent;

if (handler != null)

    handler(this, EventArgs.Empty);

A warning associated with this example (that we have helped spread) is that the code is subject to potentially unsafe JIT optimizations. The ultimate source for this seems to be Juval Lowy's book, Programming .NET Components, 2nd Edition (p250):

By copying the delegate to a temporary variable, you keep a copy of the original state of the delegate, irrespective of any thread context switches. Unfortunately, however, the JIT compiler may optimize the code, eliminate the temporary variable, and use the original delegate directly. That puts you back where you started, susceptible to the race condition.

The subject may have been first raised by a post on GrantRi's weblog about AMD64 JIT optimisations. He wrote about Hashtable, but his logic is applicable to this example. He stated that the AMD64 JIT could legally ignore a local (handler) in the code above, and simply perform two reads of a field (MyEvent) instead. This could allow another thread to set the field to null, causing handler to appear to be null inside the body of the if statement—a logical contradiction (and a serious bug).

However, this blog post was written in 2004 (and the book in early 2005), before CLR 2.0 introduced a much stronger memory model with guarantees that eliminate this bug. As per a MSDN article on memory models, CLR 2.0's memory model rules include:

  1. All the rules that are contained in the ECMA model, in particular the three fundamental memory model rules as well as the ECMA rules for volatile.
  2. Reads and writes cannot be introduced.

Joe Duffy also describes the problem (for fields, not for events specifically) and states that the memory model prevents it, in Concurrent Programming on Windows, pp517-8:

As an example of when a load might be introduced, consider this code:

MyObject mo = ...;

int f = mo.field;

if (f == 0)

{

    // do something

    Console.WriteLine(f);

}

If the period of time between the initial read of mo.field into variable f and the subsequent use of f in the Console.WriteLine was long enough, a compiler may decide it would be more efficient to reread mo.field twice. ... Doing this would be a problem if mo is a heap object and threads are writing concurrently to mo.field. The if-block may contain code that assumes the value read into f remained 0, and the introduction of reads could break this assumption. In addition to prohibiting this for volatile variables, the .NET memory model prohibits it for ordinary variables referring to GC heap memory too.

Since reads can't be introduced for fields in CLR 2.0, there's no need to use a non-inlineable helper method to raise the event; the JIT won't perform aggressive optimisations that violate the memory model. The typical code that's given for raising events is correct. (But if you do happen to be programming for a non-Microsoft CLR that only follows the ECMA rules, and your code is running on a processor with a notoriously weak hardware memory model, such as the Intel Itanium, you would need to protect this code appropriately.)

Posted by Bradley Grainger at 4:42 PM | Comments (0) | TrackBack

November 3, 2008

Tuples in .NET

It was announced at PDC that .NET 4.0 will include a Tuple implementation. Several examples of Tuple have been posted on the web; we have also written our own, which is reproduced below.

It would be nice if the BCL team would post an official Tuple design document, so that .NET 3.5 developers can ensure that any implementations they create are easy to migrate to .NET 4.0. Since that's not currently available, and the .NET 4.0 CTP doesn't have a public Tuple type, I examined the CTP bits with Reflector. I was gratified to see that our overall design matches the current CTP: Tuple<> is an immutable struct implementing IEquatable<Tuple<>>, the values are named .First and .Second, and there's a static Tuple.Create method to create new instances.

The .NET 4.0 implementation is more complex than ours (for example, due to the need to interop with IronPython, IronRuby, and F#); they also implement IComparable<Tuple<>>, whereas we have (for now) chosen to implement CompareTo as an extension method.

Update: The beta documentation for Tuple shows a different design: Tuple is an immutable class with properties named .Item1 and .Item2; IEquatable<> has been removed and replaced with IStructuralEquatable.

The Tuple<T1, T2> class is as follows. Tuples of different arity are constructed very similarly.

/// <summary>

/// A tuple comprising two items.

/// </summary>

/// <typeparam name="T1">The type of the first item in the tuple.</typeparam>

/// <typeparam name="T2">The type of the second item in the tuple.</typeparam>

[DebuggerDisplay("First={m_t1}, Second={m_t2}")]

public struct Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>

{

    /// <summary>

    /// Initializes a new instance of the <see cref="Tuple{T1,T2}"/> class.

    /// </summary>

    /// <param name="first">The first item in the tuple.</param>

    /// <param name="second">The second item in the tuple.</param>

    public Tuple(T1 first, T2 second)

    {

        m_t1 = first;

        m_t2 = second;

    }

 

    /// <summary>

    /// Gets the first item in the tuple.

    /// </summary>

    /// <value>The first item in the tuple.</value>

    public T1 First

    {

        get { return m_t1; }

    }

 

    /// <summary>

    /// Gets the second item in the tuple.

    /// </summary>

    /// <value>The second item in the tuple.</value>

    public T2 Second

    {

        get { return m_t2; }

    }

 

    /// <summary>

    /// Indicates whether the current tuple is equal to another tuple.

    /// </summary>

    /// <param name="other">A tuple to compare with this tuple.</param>

    /// <returns>true if the current tuple is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>

    public bool Equals(Tuple<T1, T2> other)

    {

        return EqualityComparer<T1>.Default.Equals(m_t1, other.m_t1) &&

            EqualityComparer<T2>.Default.Equals(m_t2, other.m_t2);

    }

 

    /// <summary>

    /// Determines whether the specified <see cref="Object"/> is equal to the current <see cref="Object"/>.

    /// </summary>

    /// <param name="obj">The <see cref="Object"/> to compare with the current <see cref="Object"/>.</param>

    /// <returns>

    /// true if the specified <see cref="Object"/> is equal to the current <see cref="Object"/>; otherwise, false.

    /// </returns>

    public override bool Equals(object obj)

    {

        return obj is Tuple<T1, T2> && Equals((Tuple<T1, T2>) obj);

    }

 

    /// <summary>

    /// Returns a hash code for this tuple.

    /// </summary>

    /// <returns>A hash code for the current <see cref="Object"/>.</returns>

    public override int GetHashCode()

    {

        return EqualityComparer<T1>.Default.GetHashCode(m_t1) ^

            EqualityComparer<T2>.Default.GetHashCode(m_t2);

    }

 

    /// <summary>

    /// Compares two tuples for equality.

    /// </summary>

    /// <param name="left">The first tuple.</param>

    /// <param name="right">The second tuple.</param>

    /// <returns><c>true</c> if the tuples are equal; false otherwise.</returns>

    public static bool operator ==(Tuple<T1, T2> left, Tuple<T1, T2> right)

    {

        return left.Equals(right);

    }

 

    /// <summary>

    /// Compares two tuples for inequality.

    /// </summary>

    /// <param name="left">The first tuple.</param>

    /// <param name="right">The second tuple.</param>

    /// <returns><c>true</c> if the tuples are not equal; false otherwise.</returns>

    public static bool operator !=(Tuple<T1, T2> left, Tuple<T1, T2> right)

    {

        return !left.Equals(right);

    }

 

    /// <summary>

    /// Returns a <see cref="String"/> that represents the current <see cref="Object"/>.

    /// </summary>

    /// <returns>A <see cref="String"/> that represents the current <see cref="Object"/>.</returns>

    public override string ToString()

    {

        return string.Format(CultureInfo.InvariantCulture, "({0},{1})", m_t1, m_t2);

    }

 

    readonly T1 m_t1;

    readonly T2 m_t2;

}

We also have a static Tuple class (with no generic parameters itself) that provides some helper methods.

/// <summary>

/// Methods for creating and manipulating Tuples.

/// </summary>

public static class Tuple

{

    /// <summary>

    /// Creates a new <see cref="Tuple{T1,T2}"/>.

    /// </summary>

    /// <param name="first">The first item in the tuple.</param>

    /// <param name="second">The second item in the tuple.</param>

    /// <returns>A new tuple consisting of the specified two items.</returns>

    public static Tuple<T1, T2> Create<T1, T2>(T1 first, T2 second)

    {

        return new Tuple<T1, T2>(first, second);

    }

 

    /// <summary>

    /// Compares the specified tuples by comparing their first component, and then (if that is equal)

    /// the second component.

    /// </summary>

    /// <param name="left">The left tuple.</param>

    /// <param name="right">The right tuple.</param>

    /// <returns>A 32-bit signed integer that indicates the relative order of the tuples being compared.</returns>

    public static int CompareTo<T1, T2>(this Tuple<T1, T2> left, Tuple<T1, T2> right)

    {

        int result = Comparer<T1>.Default.Compare(left.First, right.First);

        return result == 0 ? Comparer<T2>.Default.Compare(left.Second, right.Second) : result;

    }

}

Posted by Bradley Grainger at 8:41 AM | Comments (0) | TrackBack