Description
It's possible for two threads to try to complete a task_completion_event at the same time, where one calls set_exception(), and the other calls set().
A problem occurs if the first thread calling _StoreException() (via set_exception()) successfully completes, storing the exception holder, but then the second thread calling set() gets its timeslice before the first thread can call _CancelInternal().
In this case, the call to set() will enter the critical section and set the stack-based _RunContinuations flag to true, which will then subsequently cause the non-empty exception holder to be released. If this is the only reference to the exception holder (set via the first thread), the process will abort in debug builds via _REPORT_PPLTASK_UNOBSERVED_EXCEPTION.
So the possible order of operations is this:
thread1...
set_exception(...)
_Cancel(...)
if (_StoreException(...)) // returns true
thread 2...
set(...)
...
// following call reports unobserved exception,
// even though tce is not in a canceled state
_M_Impl->_M_exceptionHolder.reset();
thread 1...
// following call returns false,
// even though _StoreException() returned true
_Canceled = _CancelInternal();