📄 taskcollection.cpp
字号:
}
return !fThrow;
}
/// <summary>
/// Destructs a new unstructured task collection
/// </summary>
_TaskCollection::~_TaskCollection()
{
bool fThrow = false;
//
// Direct alias destruction should not attempt to go through any wait/abort cycle. It's simply the deletion/abandonment
// of the alias. The original collection might not even be around to touch.
//
if (!_IsDirectAlias())
{
fThrow = !_TaskCleanup(false);
//
// Go through and cleanup direct aliases. Note that there's an inherent problem and conflict here:
//
// - An internal context may go away and need to destroy its alias table -- it cannot touch the original task collection since it does
// not know when that collection may be deleted (it may have already)
//
// - The original task collection may be deleted but it cannot remove entries from alias tables.
//
// In order to resolve this and appropriately free the aliases, there's a simple cleanup state machine with a set of rules to act
// as a last man out frees the object.
//
// - If the destructor runs, it flags each alias so that the context can delete them
// - When a context exits, it flags each alias so that the destructor deletes them
// - First one to reach an alias with the flag set frees it.
//
// Note this is essentially a fixed reference count of two, but done with a bit flag to allow for other shared state in the flags.
//
if (_M_pOriginalCollection == this)
{
_TaskCollection *pAlias = _M_pNextAlias;
_TaskCollection *pNext = NULL;
for (; pAlias; pAlias = pNext)
{
pNext = pAlias->_M_pNextAlias;
pAlias->_ReleaseAlias();
}
}
}
TaskStack *pStack = reinterpret_cast<TaskStack *> (_M_pTaskExtension);
if (pStack) delete pStack;
if (fThrow)
throw missing_wait();
}
/// <summary>
/// Constructs a new unstructured task collection as an alias of an already existing one. An alias in this particular
/// case is a context-local representation of the original task collection.
/// </summary>
/// <param name="pOriginCollection">
/// The source of the aliasing. The newly constructed collection will be a direct or indirect
/// alias of this collection
/// </param>
/// <param name="fDirectAlias">
/// Indicates whether the collection is a direct alias (the collection is used on an arbitrary thread
/// not related to stolen work) or an indirect alias (a collection implicitly created for stolen chores).
/// </param>
_TaskCollection::_TaskCollection(_TaskCollection *pOriginCollection, bool fDirectAlias) :
_M_pOriginalCollection(pOriginCollection->_M_pOriginalCollection),
_M_pTaskExtension(NULL),
_M_executionStatus(TASKCOLLECTION_EXECUTION_STATUS_CLEAR),
_M_flags(0),
_M_stackPos(0)
{
//
// CurrentContext may create a context
//
_M_pOwningContext = SchedulerBase::CurrentContext();
ContextBase *pCurrentContext = reinterpret_cast<ContextBase*> (_M_pOwningContext);
_M_pParent = pCurrentContext->GetExecutingCollection();
_Initialize();
_M_event.set();
if (fDirectAlias)
{
_TaskCollection *pAlias = _M_pOriginalCollection->_M_pNextAlias;
for (;;)
{
_M_pNextAlias = pAlias;
_TaskCollection *pxchgAlias = reinterpret_cast <_TaskCollection *> (InterlockedCompareExchangePointer((volatile PVOID*)&_M_pOriginalCollection->_M_pNextAlias, this, pAlias));
if (pxchgAlias == pAlias)
break;
pAlias = pxchgAlias;
}
}
else
{
_M_flags |= TASKCOLLECTIONFLAG_ALIAS_IS_INDIRECT;
_M_pNextAlias = NULL;
}
_M_boundQueueId = SchedulerBase::FastCurrentContext()->GetWorkQueueIdentity();
_M_inlineFlags = 0;
}
/// <summary>
/// Determines whether the alias is stale (waiting to be deleted)
/// </summary>
bool _TaskCollection::_IsStaleAlias() const
{
ASSERT (_IsAlias());
return (_M_flags & TASKCOLLECTIONFLAG_ALIAS_FREE_ON_VIEW) != 0;
}
/// <summary>
/// Releases an alias (frees it if appropriate)
/// </summary>
void _TaskCollection::_ReleaseAlias()
{
ASSERT (_IsAlias());
long flags = _M_flags;
//
// Future proof against usage of the flags field.
//
for (;;)
{
//
// If we observed the flag but weren't the one to set it, we're responsible for freeing the alias.
//
if (flags & TASKCOLLECTIONFLAG_ALIAS_FREE_ON_VIEW)
break;
long xchgFlags = InterlockedCompareExchange(&_M_flags, flags | TASKCOLLECTIONFLAG_ALIAS_FREE_ON_VIEW, flags);
if (xchgFlags == flags)
{
//
// If we get here, *this* is poison.
//
return;
}
flags = xchgFlags;
}
delete this;
}
/// <summary>
/// Returns the original task collection (the collection that this object is an alias for).
/// </summary>
_TaskCollection *_TaskCollection::_OriginalCollection() const
{
ASSERT(_IsAlias());
return _M_pOriginalCollection;
}
/// <summary>
/// Returns the alias for the specified task collection on the current context. A NULL return would indicate
/// an error condition (e.g.: inability to allocate a new direct alias, etc...).
/// </summary>
/// <returns>
/// The alias for the specified task collection on the current context or NULL on error
/// </returns>
_TaskCollection *_TaskCollection::_Alias()
{
ASSERT(!_IsDirectAlias());
//
// Someone may have used this task collection on an arbitrary new thread -- hence, we need to make sure there's
// a current context (not FastCurrentContext). Note that such usage will imply a direct alias (the code
// will fall through to that point)
//
// Note that a task collection is bound to both the thread and the work queue. Normally, these won't differ, but may
// in certain cases where a task collection is used on an internal context which exits before deletion and we get into
// detached work queue cases. Those queues get deleted when empty and it's entirely possible that another queue
// could get reallocated in the exact same memory location. Hence -- we bind to an identity assigned to each
// work queue. Thus, aliasing checks both the owning context and the queue identity.
//
ContextBase *pCurrentContext = SchedulerBase::CurrentContext();
DWORD queueId = pCurrentContext->GetWorkQueueIdentity();
if (pCurrentContext != reinterpret_cast<ContextBase *> (_M_pOwningContext) || queueId != _M_boundQueueId)
{
//
// The task collection has been used on an alternate thread. We need an alias for the task collection. The alias can
// take one of two forms: a direct alias (the collection is used on an arbitrary thread) or an indirect alias
// (the collection is used during a stolen chore).
//
// Indirect aliases are simple: they have the lifetime (and wait span) of the stolen chore. Direct aliases
// have far more complication.
//
_TaskCollection *pIndirectAlias = pCurrentContext->GetIndirectAlias();
if (pIndirectAlias != NULL)
{
if (pIndirectAlias->_M_pOriginalCollection == this)
return pIndirectAlias;
//
// It's still possible that this follows the the pattern used by indirect aliases. It could be transitive:
//
// _TaskCollection rtp;
// rtp.Schedule(
// {
// _TaskCollection tp;
// tp.Schedule(
// {
// rtp.Schedule(...); // <-- this is transitive.
// rtp.Cancel(...); // <-- this is transitive.
// }
// });
//
// The unfortunate reality of this situation is that indirect aliasing cannot work here (see below). We need
// a direct alias.
//
// Second generation or older transitivity: While the indirect alias could be used for this to satisfy the wait,
// it would lead to deadlock and unexpected behavior if there are out-of-band dependencies between the code after the wait and the
// whatever we add to the transitive object. For example,
//
// A -> B -> C
//
// If C does A.Schedule(x);
// x == { receive_message(); }
// and someone in the middle does B.Wait(); send_message();
//
// using the indirect alias would deadlock because C would wait on x, B waits on C, and after B waits on C, x is satisfied.
//
// Hence -- we must use a direct alias in this case.
//
}
ASSERT(!_IsAlias());
_TaskCollection *pAlias = pCurrentContext->GetArbitraryAlias(this);
if (pAlias != NULL)
{
//
// Make certain the alias we are returning to the client is an alias for the task collection and thread we think it is and that it is **NOT**
// stale. Stale would imply that either the this pointer was deleted (bad) or that the context underlying the alias was deleted (bad). In any
// of these cases, there's an issue with the alias we are returning and the caller will corrupt another thread's data structure.
//
ASSERT(pAlias->_M_pOriginalCollection == this && reinterpret_cast<ContextBase *>(pAlias->_M_pOwningContext) == pCurrentContext && !pAlias->_IsStaleAlias());
return pAlias;
}
//
// At this stage, we are forced to create a direct alias.
//
_TaskCollection *pDirectAlias = new _TaskCollection(this, true);
pCurrentContext->AddArbitraryAlias(this, pDirectAlias);
return pDirectAlias;
}
return this;
}
/// <summary>
/// Returns whether the task collection is an alias.
/// </summary>
bool _TaskCollection::_IsAlias() const
{
return (_M_pOriginalCollection != this);
}
/// <summary>
/// Returns whether the task collection is an indirect alias.
/// </summary>
bool _TaskCollection::_IsIndirectAlias() const
{
return (_M_pOriginalCollection != this && (_M_flags & TASKCOLLECTIONFLAG_ALIAS_IS_INDIRECT) != 0);
}
/// <summary>
/// Returns whether the task collection has a direct alias
/// </summary>
bool _TaskCollection::_HasDirectAlias() const
{
return (_M_pOriginalCollection->_M_pNextAlias != NULL);
}
/// <summary>
/// Returns whether the task collection is a direct alias.
/// </summary>
bool _TaskCollection::_IsDirectAlias() const
{
return (_M_pOriginalCollection != this && (_M_flags & TASKCOLLECTIONFLAG_ALIAS_IS_INDIRECT) == 0);
}
/// <summary>
/// Returns whether this task collection is marked for abnormal exit.
/// </summary>
bool _TaskCollection::_IsMarkedForAbnormalExit() const
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -