Concurrency Approaches Contrasted
December 10th, 2005

Here are a few roughly-equivalent class declarations using different languages and libraries. I say “roughly equivalent” because the implementation details and performance characteristics may actually be quite different in each case. However, the end result of each is that a function on a member variable is called in a non-blocking fashion, synchronized (presumably against other operations on the same class) with the use of some resource A.


    // Vanilla C# with locks
    public class FooBar
    {
        private object A = new object();
        private T _member = new T();
        private delegate void FooBarDel();
    
        public void Foo()
        {
            FooBarDel del = new FooBarDel(FooBar);
            del.BeginInvoke(
              new AsyncCallback(FooBarDone), del);
        }
    
        private void FooBar()
        {
            lock(A)
            {
                _member.DoSomething();
            }
        }
    
        // You can typically get away without this, but
        // the documentation recommends otherwise.
        private void FooBarDone(IAsyncResult r)
        {
            FooBarDel d = r.AsyncState;
            d.EndInvoke(r);
        }
    }
    
    // Comega using a chord join pattern
    public class FooBar
    {
        private T _member = new T();
        private async A();
        public async Foo() & A()
        {
            _member.DoSomething();
            A();
        }
    
        public FooBar()
        {
            A();
        }
    }
    
    // C# 2.0+ using CCR
    public class FooBar
    {
        private T _member = new T();
        private Port<int> _p = new Port<int>();
    
        public void Foo()
        {
            _p.Post(1);
        }
    
        public FooBar()
        {
            activate(!_p.with(delegate(int i)
            {
                _member.DoSomething();
            }));
        }
    }

  

Looking at the other two examples I think it’s clear that C# as it stands today is lacking. That’s not exactly a groundbreaking statement, I realize, but I haven’t seen many examples putting all of these together in one spot.

Comega is very elegant in a situation as simple as this. CCR also seems less mistake-prone than the plain C# version, but the example I picked isn’t where CCR really shines.

(One thing I should say is that I’m not sure that the CCR example is correct or even compiles, since the library isn’t public yet. I worked this out from reading the whitepaper [pdf]. It’s only a few pages, so I recommend giving it a read.)

The goal of CCR has been to allow complex constructs such as this (also from the whitepaper) to be made easily, and dynamically if necessary:


    // The finish operation or the update operation
    // execute with exclusive control, whereas the
    // GetState and QueryState operations execute
    // concurrently. Operations of either type are
    // interleaved safely (using the '^' operator).
    activate(exclusive(p.with(DoneHandler),
        !p.with(UpdateState))
        ^
        concurrent(!p.with(GetState),
        !p.with(QueryState))
    );

  

Comega, on the other hand, provides static language features. The good news is that since CCR is just a library, it would be usable from a future C# (4.0, one would hope) that incorporated some of the ideas in Comega at the language level for the easier cases.

I had thought it was a shame that none of the Comega concurrency constructs had made it into the 3.0 C# specification. However, with CCR I think I can see why this is the case. CCR, or something like it, should offer a lot of power and flexibility well within the 3.0 timeframe without taking the risk of baking the ‘wrong’ keywords into C# itself.


Making a DataSet Read Only
December 7th, 2005

Let’s say you’re writing a component that makes a DataSet available to a number of clients. The DataSet is expected to persist in your application for a while, and be used in code written by many different developers.

You could carefully craft an email explaining that,

Hey everybody, this DataSet is shared. Please don’t change the data in it for the purposes of your own piece of the application. That could create some odd bugs that would be really hard to track down. If you need to do something like that, please make a copy of the DataSet first.

I suppose you could do that, if you wanted to waste your time. You could always create a copy of the DataSet before handing it over to anybody, but that may introduce some unnecessary performance issues if the DataSet is large. It feels a little like punishing everyone for the bad behavior of a few. If you wanted to actually prevent such problems, you could write a clever class like this that acts like a “lock” on the DataSet.


    /// <summary>
    /// Class that ensures that clients keep their pesky hands
    /// off of a particular dataset.
    /// </summary>
    internal class DataSetLock
    {
        [Conditional("DEBUG")]
        public static void Install(DataSet ds)
        {
            if (ds == null)
            {
                throw new ArgumentNullException("ds");
            }
            EventHandler thrower = delegate(object sender, EventArgs e)
            {
                string msg = string.Format(
                    "You can't change: {0}.", ds.DataSetName);
                throw new InvalidOperationException(msg);
            };
    
            foreach (DataTable t in ds.Tables)
            {
                t.RowChanging += new DataRowChangeEventHandler(thrower);
                t.RowDeleted += new DataRowChangeEventHandler(thrower);
                t.ColumnChanging += new DataColumnChangeEventHandler(thrower);
                t.TableClearing += new DataTableClearEventHandler(thrower);
                t.TableNewRow += new DataTableNewRowEventHandler(thrower);
            }
        }
        private DataSetLock() { }
    }

  

The “lock” is really an anonymous method that we attach, willy nilly, to all of the change events in the DataSet. We use lexical closure to add the DataSet name passed in to the exception message dozens of caffeine-addled developer brains will be processing shortly. [Note: if you don’t think it’s lexical closure, go talk to this guy. He’s really into the topic.]

I put the ConditionalAttribute on the Install method as well, so as long as you have reasonably good testing coverage this check will just be compiled away in retail code.

Here’s a quick usage example:


    static void Main(string[] args)
    {
        DataSet ds = new DataSet();
        ds.DataSetName = "My Data Set";
        DataTable t = ds.Tables.Add();
        t.Columns.Add("foo", typeof(string));
    
        DataSetLock.Install(ds);
    
        // Pow!
        t.LoadDataRow(new object[] { "asdf" }, true);
    }

  

This prints:

System.InvalidOperationException: You can't change: My Data Set.

Enjoy.


A Plea for Change in Sports Commentary
December 3rd, 2005

Here is one football statistic that I never want to hear again:

Team X is 40-0 when they have someone rush for 100 yards.

The conclusion you are supposed to draw is that Team X should try to run the football. Here’s one way you could rephrase the statistic:

Team X does a pretty good job of winning when they’re winning.

It’s post hoc reasoning, people! Come on! I’m starting to think Howard Cosell was on to something when he spent years hating on Frank Gifford because he was not a trained sports journalist. I don’t have a high opinion of journalists either, but now the Giffords are the norm in the commentary business. The intelligence of the analysis suffers. I’m all for coaches-as-commentators, but I think we should all agree that players-as-commentators is not working out.