Concurrent Programming on Windows (by Joe Duffy) is a book so good I had to put it down, frequently, to stop and think. The information density is pretty high and I often found myself staring blankly into space for minutes at a time, book open on my lap, thinking through what I’d just read.
This is probably the definitive book on concurrency in Windows, covering general principles and the relevant APIs across both native (Win32) and managed (.NET). It has a good balance of theoretical discussion and practical advice, with no shortage of references at the end of each chapter for those who feel inclined for some additional background reading. (For instance, the “Further Reading” section at the end of Chapter 10 “Memory Models and Lock Freedom” points to some light reading: AMD x86-64 Architecture Programmer’s Manual, Volumes 1–5 (!))
What makes this book truly valuable is the amount of information and knowledge that it aggregates, from obscure technical sources, academic papers, and even first-hand spelunking in the Windows source code to find answers to some undocumented behavioural details. It also provides plenty of practical advice garnered from years of experience.
For me, having mainly managed programming experience, this book provided a nice opportunity to understand more about the underlying obscurities of Win32, and how these relate to and contrast with what is exposed in .NET. Having that underlying knowledge has let me see how passing the Invalid Wait Handle value to some asynchronous methods can make them execute synchronously instead, and to understand that asynchronous I/O needs to be decided on when a file handle is opened — details that had previously eluded me in my journeys through managed-land.
Other things that were interesting to learn about included lock-free algorithms (with clever tricks like structuring a lock-free linked list such that it has a sentinel node when empty, cunningly avoiding the problem of updating two pointers when the list transitions between empty and non-empty), and the details of kernel-mode synchronisation primitives, with their limitless caveats (the abandoned mutex scenario was my favourite… when waiting on a named mutex it is possible that it would have been abandoned if another process exited before releasing it. Despite returning an error, the operation has succeeded in acquiring the mutex and you must still remember to release it! As if you didn’t have enough to think about by that point, with all the other complexities around alertable waits and pumping the message queue if you’re in an STA).
I previously said that, at a length of 736 pages, CLR via C# (2nd Edition) was the largest book I have ever read. But with a length of 930 pages, Concurrent Programming on Windows has surpassed this. Next up on the reading list: CLR via C# (3rd Edition).