Today I’m once again asking for your help.
The headliner feature for C# 5 was of course the await operator for asynchronous methods. The intention of the feature was to make it easier to write asynchronous programs where the program text is not a mass of inside-out code that emphasizes the mechanisms of asynchrony, but rather straightforward, easy-to-read code; the compiler can deal with the mess of turning the code into a form of continuation passing style.
However, asynchrony is still hard to understand, and making methods that allow other code to run before their postconditions are met makes for all manner of interesting possible bugs. There are a lot of great articles out there giving good advice on how to avoid some of the pitfalls, such as:
- Psychic debugging of async methods
- Pitfalls to avoid when passing async lambdas
- Async gotchas
- Don’t mix await and compound assignment
- Don’t block on async code
These are all great advice, but it is not always clear which of these potential defects are merely theoretical, and which you have seen and fixed in actual production code. That’s what I am very interested to learn from you all: what mistakes were made by real people, how were they discovered, and what was the fix?
Here’s an example of what I mean. This defect was found in real-world code; obviously the extraneous details have been removed:
Frob GetFrob() { Frob result = null; var networkDevice = new NetworkDevice(); networkDevice.OnDownload += async (state) => { result = await Frob.DeserializeStateAsync(state); }; networkDevice.GetSerializedState(); // Synchronous return result; }
The network device synchronously downloads the serialized state of a Frob
. When it is done, the delegate stored in OnDownload
runs synchronously, and is passed the state that was just downloaded. But since it is itself asynchronous, the event handler starts deserializing the state asynchronously, and returns immediately. We now have a race between GetFrob
returning null, and the mutation of closed-over local result
, a race almost certainly won by returning null.
If you’d rather not leave comments here — and frankly, the comment system isn’t much good for code snippets anyways — feel free to email me at eric@lippert.com
. If I get some good examples I’ll follow up with a post describing the defects.