// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// TaskCollectionBase.cpp
//
// General abstract collection of work counting / eventing implementation
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "concrtinternal.h"
namespace Concurrency
{
namespace details
{
///
/// Called when an exception is raised on a chore on a given task collection, this makes a determination of what to do with the exception
/// and stores it for potential transport back to the thread performing a join on a task collection.
///
void _TaskCollectionBase::_RaisedException()
{
//
// Current strategy is that the first exception in is kept and rethrown. We may update this in the future.
//
void * _OldStatus = _M_pException;
for (;;)
{
//
// We always overwrite the cancel exception being here. Everything else is "more important".
//
std::exception_ptr *_pException = (std::exception_ptr *)((size_t)_OldStatus & ~_S_cancelBitsMask);
if (_pException != NULL && (size_t)_pException != _S_cancelException)
return;
//
// Maintain the lower bit as a cancel status flag to determine where to stop a cancellation.
//
size_t _cancelStatus = ((size_t)_OldStatus & _S_cancelBitsMask);
void * _XchgStatus = _InterlockedCompareExchangePointer((void * volatile *) &_M_pException, (void *) (_S_nonNull | _cancelStatus), _OldStatus);
if (_XchgStatus == _OldStatus)
break;
_OldStatus = _XchgStatus;
}
//
// Note that this is safe as this will only be called on a chore executing on the collection; therefore it will not be touched by the forking
// thread until after we "_CountUp" which comes after this.
//
void *_pExc = _concrt_new std::exception_ptr(std::current_exception());
_OldStatus = _M_pException;
for(;;)
{
size_t _cancelStatus = ((size_t)_OldStatus & _S_cancelBitsMask);
void *_pExcWC = (void *)((size_t)_pExc | _cancelStatus);
void *_XchgStatus = _InterlockedCompareExchangePointer((void * volatile *) &_M_pException, _pExcWC, _OldStatus);
if (_XchgStatus == _OldStatus)
break;
_OldStatus = _XchgStatus;
}
}
///
/// Potentially rethrows the exception which was set with _RaisedException. The caller has responsibility to ensure that _RaisedException
/// was called prior to calling this and that _M_pException has progressed beyond the _S_nonNull state.
///
void _TaskCollectionBase::_RethrowException()
{
//
// The cancellation exception is treated very specially within the runtime. Do not arbitrarily rethrow from here.
//
std::exception_ptr *_pException = _Exception();
if (_pException != NULL && (size_t)_pException != _S_cancelException)
{
std::exception_ptr _curException = *_Exception();
delete _pException;
_M_pException = NULL;
if ( !__uncaught_exception())
std::rethrow_exception(_curException);
}
}
///
/// Marks the collection for cancellation and returns whether the collection was thus marked.
///
bool _TaskCollectionBase::_MarkCancellation()
{
void *_OldStatus = _M_pException;
for(;;)
{
if ((size_t)_OldStatus & _S_cancelBitsMask) // already canceled
return false;
void *_XchgStatus = _InterlockedCompareExchangePointer((void * volatile *) &_M_pException,
(void *)((size_t)_OldStatus | _S_cancelStarted),
_OldStatus);
if (_XchgStatus == _OldStatus)
return true;
_OldStatus = _XchgStatus;
}
}
///
/// Finishes the cancellation state (changing from _S_cancelStarted to one of the other states). Note that only the
/// thread which successfully marked cancellation may call this.
///
void _TaskCollectionBase::_FinishCancelState(size_t _NewCancelState)
{
ASSERT(_CancelState() == _S_cancelStarted);
ASSERT(_NewCancelState != _S_cancelNone && _NewCancelState != _S_cancelStarted);
void *_OldStatus = _M_pException;
for(;;)
{
void *_XchgStatus = _InterlockedCompareExchangePointer((void * volatile *) &_M_pException,
(void *)(((size_t)_OldStatus & ~_S_cancelBitsMask) | _NewCancelState),
_OldStatus);
if (_XchgStatus == _OldStatus)
break;
_OldStatus = _XchgStatus;
}
}
///
/// Called when a cancellation is raised on a chore on a given task collection. This makes a determination of what to do with the exception
/// and stores it for potential transport back to the thread performing a join on a chore collection. Note that every other exception
/// has precedence over a cancellation.
///
void _TaskCollectionBase::_RaisedCancel()
{
void *_OldStatus = _M_pException;
for (;;)
{
std::exception_ptr *_pException = (std::exception_ptr *)((size_t)_OldStatus & ~_S_cancelBitsMask);
if (_pException != NULL)
return;
size_t _cancelStatus = ((size_t)_OldStatus & _S_cancelBitsMask);
void *pExcWC = (void *)(_S_cancelException | _cancelStatus);
void *_XchgStatus = _InterlockedCompareExchangePointer((void * volatile *) &_M_pException, pExcWC, _OldStatus);
if (_XchgStatus == _OldStatus)
break;
_OldStatus = _XchgStatus;
}
}
///
/// Called in order to determine whether this task collection will interrupt for a pending cancellation at or above it.
///
bool _TaskCollectionBase::_WillInterruptForPendingCancel()
{
//
// We can only perform the interruption point if someone in the parentage chain is actually inlined. The number of times where we get here
// without such should be minimal.
//
// Note that structured collections do not initialize _M_pParent until they are inlined. In order to avoid excess initialization in the
// structured case, we key off that to determine the validity of the field. Note that this check is perfectly okay for task collections
// as well.
//
_TaskCollectionBase *pParent = _SafeGetParent();
_CancellationTokenState *pTokenState = _GetTokenState();
while (pParent != NULL)
{
//
// If this token is non null- it could hide cancellation from a parent task collection.
//
if (pTokenState == NULL)
{
if ((pParent->_IsStructured() && (static_cast<_StructuredTaskCollection *>(pParent))->_IsMarkedForCancellation()) ||
(!pParent->_IsStructured() && (static_cast<_TaskCollection *>(pParent))->_IsMarkedForAbnormalExit()))
return true;
}
else
{
if (pTokenState == _CancellationTokenState::_None())
return false;
else
return pTokenState->_IsCanceled();
}
//
// Grab the parent token before switching to its parent.
//
pTokenState = pParent->_GetTokenState();
pParent = pParent->_SafeGetParent();
}
return false;
}
///
/// Returns the cancellation token state associated with this task collection.
///
_CancellationTokenState *_TaskCollectionBase::_GetTokenState(_CancellationTokenRegistration **_PRegistration)
{
_CancellationTokenState *pTokenState = _M_pTokenState;
_CancellationTokenRegistration *pRegistration = NULL;
if (reinterpret_cast(pTokenState) & TASKCOLLECTIONFLAG_POINTER_IS_REGISTRATION)
{
pRegistration = reinterpret_cast<_CancellationTokenRegistration *>(
reinterpret_cast(pTokenState) & ~TASKCOLLECTIONFLAG_POINTER_IS_REGISTRATION
);
if (pRegistration != NULL)
{
pTokenState = pRegistration->_GetToken();
}
else
{
pTokenState = _CancellationTokenState::_None();
}
}
if (_PRegistration != NULL)
{
*_PRegistration = pRegistration;
}
return pTokenState;
}
} // namespace details
} // namespace Concurrency