DCSIMG
February 2010 - Posts - All Your Base Are Belong To Us

All Your Base Are Belong To Us

Mostly .NET internals and other kinds of gory details

February 2010 - Posts

Are We Training Our Customers to Be Dumb?

During the past few years at Sela, I’ve authored several courses and participated in the development process of several dozens more. They covered a great deal of topics – ranging from the gory inner workings of Windows and the CLR, through new technologies like LINQ and Windows 7, and all the way to introductory courses to C# programming. I’ve also had the experience of designing and developing courses in a variety of styles – the “Sela style”, which focuses on the brilliance of the instructor delivering the course and slightly deemphasizes the minute details in student handbooks, the “standard international courseware style”, which is all about attention to detail and greatly elaborate lab instructions, and some combinations of the two.

I always preferred concise exercises and labs that emphasize the student’s ability to think and process the material, over exercises that involve straightforward typing or copy-pasting code from the lab manual into Visual Studio and seeing it compile. And I always wondered if it was just some mentality thing here in Israel, or a personal preference I have because I like teaching difficult courses to an audience of highly experienced developers. I also wondered if the reason some customers prefer and training companies encourage the drill-down, copy-paste labs was a matter of dumbing down the lab and making sure everyone in the classroom is capable of finishing it within the allotted time; not to mention that a short and concise lab definition might seem vague and lacking in detail to some audiences.

But one thing just occurred to me. Take a look at this exercise from an introductory mathematical analysis course, one of the first things I found by looking up “mathematical analysis homework” on Google:

Let f be a monotone function on an interval. Prove that f is continuous iff its image is an interval.

Can you see what I’m getting at? OK, here’s an example of an exercise I used a few weeks ago when teaching a concurrent programming course:

The reader-writer lock design shown in class uses two mutexes and an event.  Convert this design to use only a single semaphore. Prove that there is a possible deadlock if multiple writers attempt to acquire the lock. Fix the problem by introducing a mutex (additionally to the semaphore).

Compare and contrast to this hypothetical example from today’s typical software industry “courseware”:

In this task, you will implement the IComparable interface so that instances of your class can be sorted by external sort algorithms. Open the MyClass.cs file in Visual Studio, and navigate to the MyClass class definition. (To open a file, click File –> Open.)

Declare that your class is implementing the IComparable interface using the interface implementation syntax, as follows:

internal class MyClass : IComparable {

Implement the CompareTo method. To compare the parameter to your object, cast the parameter to an instance of MyClass. Next, compare the values of the X and Y properties of the parameter to the values of the X and Y properties of your instance (this).

[…several pages later…]

Congratulations! In this exercise, you have implemented the IComparable interface so that users of your class can compare instances of it without being aware of your class’ implementation details. For more information about the IComparable interface, consult the MSDN documentation at […]

It’s mind-numbing to read just this passage, not to mention the 30+ pages of standardized jargon it is usually surrounded by. Are we deliberately dumbing down our training materials and by extension the developer community? What’s the ultimate objective of having every step of the way explained in excruciating detail? How is it possible to learn anything without even trying to perform the task on your own before copy-pasting code from the student manual?

I’ve officially given up trying to understand this phenomenon. But I do know this: Given the choice between short and concise instructions and a 30+ page lab document, I would choose the former over the latter even if it were a million times harder.

I feel that a good litmus test for a properly designed lab exercise would be the following relation:

Lines of Text in Lab Manual / Lines of Code Written by Student < 0.1

Another Happy User of Windows Home Server

A couple of months ago I bought an Acer Aspire easyStore home server for my home. It comes preinstalled with Windows Home Server. The configuration I bought has a single 1TB hard drive with the OS itself and plenty of room for backups and data; I also ordered another internal WD Caviar Green 1.5TB hard drive and an external 2TB Seagate hard drive.

The internal hard drive was faulty from the start, so I’m in the process of replacing it, but right now I’m working with 3TB of space, which is enough for my backups and most of my data. It’s really easy to add or remove drives, and you can do it without ever shutting down the server by simply opening the front door, removing one of the bays, inserting your hard drive (screw-less design – another bonus!) and you’re good to go.

One of the neatest things about this server is its form factor – it’s really small. I have a small storage cabinet that’s 30cm (1’) wide and about 70cm (2’4”) tall, and it hosts the home server, the cable modem, the router and the external hard drive without missing a beat.

If you know me personally, you probably know that I’m the (grumpy) system administrator for seven computers (two desktops, two media centers and three laptops), and ensuring the integrity of the data, performing timely backups, restoring them after installation problems and so on – can quickly become annoying if it’s not your day job… One of the primary objectives of buying the server was to stop worrying!

image

So far I’m very happy with my home server. I’m using it for backups – and because I have Windows 7 on all seven machines, there’s plenty of duplication which Windows Home Server detects – that’s why it’s currently using only 424GB of space for backups. I’m using it for shared storage – photos, videos, music and software installations. The one thing I’m not storing on the home server yet is recorded TV, because it usually doesn’t survive more than a couple of days before being deleted, and my media center’s 1.5TB of storage are more than enough to store plenty of recorded programs.

I’m also using the home server to remotely connect to my computers at home. When you buy a Windows Home Server, you’re automatically entitled to a sub-domain (under .homeserver.com, or other alternatives) that you can use to connect to your server. You can browse all the shared folders and remotely connect to any computer at home using Remote Desktop.

image

I look back at the last few years and realize that I’ve never been very good with backups. I have backups of the most important personal stuff on DVDs, and some of the work stuff is safely backed up by LiveMesh and SkyDrive. However, if one of my PCs at home suffered a major breakdown, it would take me weeks to restore it to a decent state. With Windows Home Server, restoring over a wireless N connection or a wired Gigabit connection is a matter of hours at most.

This definitely isn’t the freshest piece of advice you’ve ever heard, but if you’re not thinking about backups, it’s better to start sooner than later, and Windows Home Server is a good place to start :-)

Race Condition When Modifying the HttpRequestMessageProperty in an Outgoing WCF Message

I’ve stumbled upon a fairly obscure race condition in WCF’s implementation of the HTTP transport, and just wanted to quickly share it with you should you ever encounter it.

I was using a simple publish/subscribe router similar to the one I’ve blogged about two years ago to automatically publish notifications to a list of subscribers. The router is a WCF service that implements a generic interface that takes a Message and creates multiple copies of it to send to all registered subscribers.

Here’s my original take on the dispatch code: [exception-handling removed for clarity]

foreach (string subscriber in subscribers)

{

    Message toSend = buffer.CreateMessage();

    IGenericOneWayContract proxy =
        ChannelFactory<IGenericOneWayContract>.CreateChannel(

            Program.Binding, new EndpointAddress(subscriber));

    proxy.Action(toSend);

    ((ICommunicationObject)proxy).Close();

    toSend.Close();

}

…now, seeing that the messages are copied and the send operation is stateless, there’s no reason why I shouldn’t be able to parallelize these operations and make sure subscribers get the notification messages more promptly:

Parallel.ForEach(

  subscribers,

  subscriber =>

      {

          Message toSend = buffer.CreateMessage();

          IGenericOneWayContract proxy =
             ChannelFactory<IGenericOneWayContract>.CreateChannel(

                Program.Binding, new EndpointAddress(subscriber));

          proxy.Action(toSend);

          ((ICommunicationObject) proxy).Close();

          toSend.Close();

      });

But unfortunately, the latter version of the code fails with the following exception, that only occurs once in several hundreds of messages on my machine: [removed parameters from stack trace for clarity]

System.NullReferenceException: Object reference not set to an instance of an object.

Server stack trace:
   at System.ServiceModel.Channels.HttpRequestMessageProperty.get_Headers()
   at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.PrepareHttpSend…
   at System.ServiceModel.Channels.HttpOutput.Send…
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest…
   at System.ServiceModel.Channels.RequestChannel.Request…
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Send…
   at System.ServiceModel.Channels.ServiceChannel.Call…
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService…
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke…

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage…
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke…
   at WCFHttpMessageProperties.IGenericOneWayContract.Action…
   at WCFHttpMessageProperties.PubSubRouter.<>c__DisplayClass1.<Action>b__0…
   at WCFHttpMessageProperties.Parallel.<>c__DisplayClass3`1.<ForEach>b__0…

After looking at the code with Reflector, it seems that during the PrepareHttpSend method that we see in the stack trace, there’s a phase where the message properties (including the HttpRequestMessageProperty) are refreshed, e.g. with HTTP header information. Why, however, should there be a problem with the parallel version of the code if I’m delivering separate copies of the message to my subscribers?

Well, from looking at the implementation of message cloning with Reflector, it appears that message properties are not cloned unless they implement the IMessageProperty interface (see MessageProperties.CreateCopyOfPropertyValue). The HttpRequestMessageProperty class does not implement this interface, and therefore “cloning” it consists of simply returning the original value.

Now the problem should be evident – we have multiple threads that are trying to modify the same HttpRequestMessageProperty instance, with its HTTP header collection and whatnot. There’s a fairly rare race condition because most of the time the operations on the HTTP headers don’t collide, and happen to leave the message property in a consistent state. When they do collide, we get the above exception.

There’s actually another variety of this problem that I’ve seen in the wild, but haven’t been able to reproduce on my machine – it’s when the message is eventually sent, but contains duplicate HTTP headers! This is obviously rather difficult to diagnose because the message is successfully sent with the duplicate headers but the subscriber refuses to receive it! The same fix was sufficient for both of the issues.

So, what’s the fix? In this case, short of writing a manual serialization mechanism for message properties, I didn’t see many options other than a workaround. The workaround I found is to remove the HttpRequestMessageProperty from the message before creating copies – this will cause the transport channel (HttpOutput) to create a new instance of the property and attach this fresh copy to the message.

In other words, the complete and working version of the code is something along the following lines:

message.Properties.Remove(HttpRequestMessageProperty.Name);

MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);

string[] subscribers = …;

Parallel.ForEach(

  subscribers,

  subscriber =>

      {

          Message toSend = buffer.CreateMessage();

          IGenericOneWayContract proxy =

             ChannelFactory<IGenericOneWayContract>.CreateChannel(

                Program.Binding, new EndpointAddress(subscriber));

          proxy.Action(toSend);

          ((ICommunicationObject) proxy).Close();

          toSend.Close();

      });

buffer.Close();

I thought it would be interesting to test this behavior with WCF 4.0. On Visual Studio 2010 Beta 2, I was able to reproduce the duplicate HTTP headers problem, with the following exception, although the problem is admittedly rare: [again, edited for clarity; I marked the duplicate header in italic]

System.ServiceModel.ProtocolException: Content Type application/soap+xml; charset=utf-8,application/soap+xml; charset=utf-8 was not supported by service http://localhost:8993/Client_6.  The client and service bindings may be mismatched. ---> System.Net.WebException: The remote server returned an error: (415) Cannot process the message because the content type 'application/soap+xml; charset=utf-8,application/soap+xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'..
   at System.Net.HttpWebRequest.GetResponse…
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply…
   --- End of inner exception stack trace ---

Server stack trace:
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException…
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply…   at System.ServiceModel.Channels.RequestChannel.Request…
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Send…
   at System.ServiceModel.Channels.ServiceChannel.Call…
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService…
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke…

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage…
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke…
   at WCFHttpMessageProperties.IGenericOneWayContract.Action…
   at WCFHttpMessageProperties.PubSubRouter.<>c__DisplayClass2.<Action>b__1…
   at WCFHttpMessageProperties.Parallel.<>c__DisplayClass4`1.<ForEach>b__1…

Finally, in another test run I encountered a third variety of this exception, that I wasn’t able to see with WCF 3.0. It’s due to an internal implementation change, but the underlying problem is still the same race condition as before: [edited for clarity]

System.ArgumentException: Item has already been added. Key in dictionary: 'Content-Type'  Key being added: 'Content-Type'

Server stack trace:
   at System.Collections.Hashtable.Insert…
   at System.Collections.Hashtable.Add…
   at System.Collections.Specialized.NameObjectCollectionBase.BaseAdd…
   at System.Collections.Specialized.NameValueCollection.Add…
   at System.Net.WebHeaderCollection.Add…
   at System.Collections.Specialized.NameValueCollection.Add…
   at System.ServiceModel.Channels.HttpRequestContext.ListenerHttpContext.System.ServiceModel.Channels.HttpRequestMessageProperty.IHttpHeaderProvider.CopyHeaders…
   at System.ServiceModel.Channels.HttpRequestMessageProperty.get_Headers…
   at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.PrepareHttpSend…
   at System.ServiceModel.Channels.HttpOutput.Send…
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest…
   at System.ServiceModel.Channels.RequestChannel.Request…
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Send…
   at System.ServiceModel.Channels.ServiceChannel.Call…
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService…
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke…

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage…
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke…
   at WCFHttpMessageProperties.IGenericOneWayContract.Action…
   at WCFHttpMessageProperties.PubSubRouter.<>c__DisplayClass2.<Action>b__1…
   at WCFHttpMessageProperties.Parallel.<>c__DisplayClass4`1.<ForEach>b__1…

This last exception even seems to hint that someone at Microsoft introduced an additional sanity check so that messages with duplicate headers do not even leave the HTTP transport, but this sanity check doesn’t work (see the previous exception) and doesn’t fix the root cause of this issue, which is the fact that WCF message properties are not properly cloned when the message is cloned.

Fortunately, the same workaround fixes the problem in WCF 4.0 a well.