[threads] Fix race between async suspend and compensation
authorLudovic Henry <ludovic@xamarin.com>
Thu, 22 Oct 2015 20:01:10 +0000 (21:01 +0100)
committerLudovic Henry <ludovic@xamarin.com>
Wed, 28 Oct 2015 14:34:56 +0000 (14:34 +0000)
commit5e52db585ec2930d106ab5b5804fd5f0282abae1
tree32b7cbbaa39b90469eb638cb10361edf978cb839
parent2684e2fc3c9ced00f839f960573b022a42ad5586
[threads] Fix race between async suspend and compensation

The race only happens on Posix with unified suspend.

The chain of event is the following:
- T1 suspend the world (calls `sgen_unified_suspend_stop_world`)
- T2 is async suspended
- T2 execute suspend_signal_handler:
 - T2 finish async suspend
 - T2->suspend_can_continue is set to FALSE as the thread is detaching
 - T2 notify T1 via `mono_threads_notify_initiator_of_suspend`
 - T2 is yielded (via the OS scheduler, or forced via a `sleep`)
- T1 returns from `mono_threads_wait_pending_operations`
- T1 does its stuff
- T1 restart the world
- T1 (or any another thread, except for T2) suspend the world (it does not
  necessarily uses `sgen_unified_suspend_stop_world`)
- T2 is async suspended: incrementation of T2->suspend_count to 2, but it is
    not signalled because it is already in ASYNC_SUSPENDED state
- T2 wakes up (for any reason) and execute compensation <- ERROR: suspend_count != 1

This race can happen because when async suspending, we notify the
suspendor before executing the compensation, in this case for a thread
which is detaching. By simply post-ponning the notification, just after
the compensation, we eliminate this race.
mono/utils/mono-threads-posix-signals.c