[corlib] Fix race condition between RegisteredWaitHandle Wait and Unregister methods
authorLudovic Henry <ludovic@xamarin.com>
Tue, 16 Feb 2016 17:03:45 +0000 (17:03 +0000)
committerLudovic Henry <ludovic@xamarin.com>
Tue, 16 Feb 2016 20:11:58 +0000 (20:11 +0000)
commit1a69330766c41831668aaa22b193d4f81aca73f0
tree5803668b115b4a634db1d0f508e9bbe62b42ba76
parent19c0bab2433aad95d2bd4cab29949caa52ae3160
[corlib] Fix race condition between RegisteredWaitHandle Wait and Unregister methods

In the following case we would have a race between Wait and Unregister methods:
 - T1 calls ThreadPool.RegisterWaitForSingleObject to wait on waitHandle
 - registeredWaitHandle is constructed to wait on waitHandle
 - registeredWaitHandle.Wait method is enqueued on the ThreadPool
 - T1 calls registeredWaitHandle.Unregister
 - T1 calls waitHandle.Close (happens in Process.StopWatchingForExit)
 - TTP (the threadpool thread) calls registeredWaitHandle.Wait
  - TTP calls WaitHandle.WaitAny (waitHandle, ...)

The last step try to access the waitHandle that has been previously closed, leading to a _wapi_handle_ref / _wapi_handle_unref_full error message.

The fix is to ensure that the RegisteredWaitHandle keeps the WaitHandle alive. To achieve this, we simply use the SafeWaitHandle inside the WaitHandle by: increasing its ref count when constructing the RegisteredWaitHandle, and decrementing it when unregistering it.
We also increase the count when using it in Wait, so if we execute Unregister and Wait concurrently, we guarantee that the WaitHandle is still valid in Wait, or get an exception if not.
mcs/class/corlib/System.Threading/RegisteredWaitHandle.cs