Monday, June 25, 2007

The threading jungle

Multi-threading has been around a long time. I first heard the term in the mid-1980s when discussing how ICL were going to host UNIX on VME. The first real implementation I saw was on Apollo NCS, a system that had many innovative features. ICL, VME, and Apollo have all gone, victims of acquisitions. Threads remain.

Multi-threading hit the mainstream with Windows NT, the system was built on it. The consensus around us UNIX hackers was that Microsoft had to encourage threads because their processes were so inefficient - no fork/exec you see. Probably utter twaddle; you know what UNIX hackers are.

Threads brought synchronisation problems. Of course they did. System programmers were no strangers to this type of issue, just the context of threads was new(ish). Way back when, on ICL VME, all we had were "test and set" and "decrement and set" instructions that were atomic. When I were a lad we had to build our own primitives, there was a particularly handy instruction that let you not only raise an interrupt (event/signal, whatever) but allowed you to send a two-word data block with it. Kids of today … don't know they're born… forty miles 't pit… etc.

I got to write real synchronisation primitives when I did the VME port of the MIMER RDBMS in the early '80s. There is nothing like having to write the damn things to understand how they work. I did not design them, I hastily add - that was done by some egg-head at Uppsala University. The problem then, as now, is not in using the APIs, it is in understanding how they interact with your application and (more important) how the application interacts with itself.

First rule when writing co-operating threads: things only go wrong at the worse possible moment. And that is usually after the thing has gone into production.

The Windows APIs are fairly easy to use, certainly compared to knitting your own. The main wrinkles in using them are a few peculiarities with the C/C++ runtime-library. I find it amazing that so many system engineers insist in using CreateThread instead of _beginthreadex. Back in Visual Studio 2.0 this was understandable, even I did it. The documentation was, um, "challenged". Now, with the MSDN, there is no excuse, the problems are well documented. Still people insist on using the wrong API. My theory, for what it is worth, is that a call to _beginthreadex, with its associated casts, looks messy on the screen, surrounded as it is by unrivalled elegance (sic). CreateThread looks good, _beginthreadex is ugly. For wotsit's sake! A thing of beauty with a memory leak is bad code, and I don't care what it looks like. Humph.

Still, you thought that was ugly? Try finding the thread-safe RTL functions in pthreads. Threads had to be retro-fitted to UNIX. Many fought against it, even the hero Torvold in the clone wars, but resistance was futile. Microsoft made life easy for us developers by having a whole special runtime-library just for multi-threading, ungrateful pups that we are. The pthreads implementations on UNIX have no such luxury. Instead there is a whole mess of different functions that are re-entrant with the _t suffix. Bye-bye portability. Finding which functions these are is a bit hit and miss, sometimes they are not in the man pages, and the standard only lists minimum requirements. Try teaching Condition Variables to a class already spaced out by mutxes. The word "predicate" makes their eyes glaze over. The fixed grin is the give-away: "Gone, solid gone", as Baloo would say. I'm sure Kipling was a coder, who else would write a whole poem about a conditional statement?

No comments: