Challenge: Find the bug fix
Usually I tend to pose bugs as the challenges, and not the bug fixes, but this is an interesting one. Take a look at the following code:
var handles = new List<WaitHandle>(); using ( var stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None, 0x1000, FileOptions.Asynchronous)) { for (int i = 0; i < 64; i++) { var handle = new ManualResetEvent(false); var bytes = Encoding.UTF8.GetBytes( i + Environment.NewLine); stream.BeginWrite(bytes, 0, bytes.Length, delegate(IAsyncResult ar) { stream.EndWrite(ar); handle.Set(); }, stream); handles.Add(handle); } WaitHandle.WaitAll(handles.ToArray()); stream.Flush(); }
Now, tell me why I am creating a ManualResetEvent manually, instead of using the one that BeginWrite IAsyncResult will return?
Comments
Is it because there are not enough ThreadPool threads (i think it defaults to 25 per cpu)?
No, that is not an issue, they will simply execute in batches of 25
Is it because of the order in which their event fires? For example, it will fire prior to the end actually occuring. It could also be that their reset event is configured differently than the one you are making, i.e. maybe their reset event is actually and AutoResentEvent.
There is relation to ordering, but not in the way you are thinking about it
stream.EndWrite() probably disposes the WaitHandle which causes WaitAll to fail.
Thomas,
Exactly.
But to make matters worse, it will only happen in async mode!
Very subtle.
The WaitHandle returned by IAsyncResult from the async write only "needs" to live until EndWrite is called. Calling WaitAll can potentially throw an ObjectDisposedException if the handle is closed.
The calling set on the manually created handle does not release the handle for GC.
oops. someone got a correct answer in before I posted. mea culpa.