// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // workqueue.h // // Work stealing queues pair implementation. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- namespace Concurrency { namespace details { #define QUEUE_ATTACHED 0 #define QUEUE_DETACHED 1 /// /// The work queue is a pair of bound work stealing queues, one structured and one unstructured, that /// can be associated with a context. /// #pragma warning(push) #pragma warning(disable: 4324) // structure was padded due to alignment specifier class WorkQueue { public: /// /// Constructs a new work queue. /// WorkQueue(); // // Queue Reuse: // /// /// Informs the WSQ what context it's attached to in a synchronized manner. /// void LockedSetOwningContext(ContextBase *pOwningContext) { m_lock._Acquire(); m_pOwningContext = pOwningContext; m_lock._Release(); } /// /// Informs the WSQ what context it's attached to. /// void SetOwningContext(ContextBase *pOwningContext) { m_pOwningContext = pOwningContext; } // // Structured Parallelism: // /// /// Pushes an unrealized chore onto the work stealing queue for structured parallelism. /// /// /// The chore to push onto the structured work stealing queue /// /// /// The mailbox slot into which the specified chore has already been queued for explicit affinitization. The slot can be an empty /// slot if the chore was not queued anywhere. /// void PushStructured(_UnrealizedChore *pChore, Mailbox<_UnrealizedChore>::Slot affinitySlot) { m_structuredQueue.Push(pChore, affinitySlot); } /// /// Pushes an unrealized chore onto the work stealing queue for structured parallelism. /// /// /// The chore to push onto the structured work stealing queue /// void PushStructured(_UnrealizedChore *pChore) { m_structuredQueue.Push(pChore); } /// /// Pops the topmost chore from the work stealing queue for unstructured parallelism. Failure /// to pop typically indicates stealing. /// /// /// An unrealized chore from the structured work stealing queue or NULL if none is present /// _UnrealizedChore* PopStructured() { return m_structuredQueue.Pop(); } /// /// Returns whether the structured work stealing queue is empty. /// bool IsStructuredEmpty() const { return m_structuredQueue.Empty(); } // // Unstructured Parallelism: // /// /// Pushes an unrealized chore onto the work stealing queue for unstructured parallelism. The returned /// value is a cookie which can be used in a call to TryPopUnstructured. /// /// /// The chore to push onto the unstructured work stealing queue /// /// /// The mailbox slot into which the specified chore has already been queued for explicit affinitization. The slot can be an empty /// slot if the chore was not queued anywhere. /// /// /// A cookie which can be used to identify the chore for a later TryPopUnstructured call /// int PushUnstructured(_UnrealizedChore *pChore, Mailbox<_UnrealizedChore>::Slot affinitySlot) { return m_unstructuredQueue.Push(pChore, affinitySlot); } /// /// Pushes an unrealized chore onto the work stealing queue for unstructured parallelism. The returned /// value is a cookie which can be used in a call to TryPopUnstructured. /// /// /// The chore to push onto the unstructured work stealing queue /// /// /// A cookie which can be used to identify the chore for a later TryPopUnstructured call /// int PushUnstructured(_UnrealizedChore *pChore) { return m_unstructuredQueue.Push(pChore); } /// /// Attempts to pop the chore specified by a cookie value from the unstructured work stealing queue. Failure /// to pop typically indicates stealing. /// /// /// A cookie returned from PushUnstructured indicating the chore to attempt to pop from /// the unstructured work stealing queue /// /// /// The specified unrealized chore (as indicated by cookie) or NULL if it could not be popped from /// the work stealing queue /// _UnrealizedChore *TryPopUnstructured(int cookie) { return m_unstructuredQueue.TryPop(cookie); } /// /// Attempts to steal an unrealized chore from the unstructured work stealing queue. /// /// /// Whether to steal the task at the bottom end of the work stealing queue even if it is an affinitized to a location /// that has active searches. This is set to true on the final SFW pass to ensure a vproc does not deactivate while there /// are chores higher up in the queue that are un-affinitized and therefore inaccessible via a mailbox. /// /// /// An unrealized chore stolen from the work stealing queues or NULL if no such chore can be stolen /// _UnrealizedChore *Steal(bool fForceStealLocalized); /// /// Attempts to steal an unrealized chore from the unstructured work stealing queue. /// /// /// Whether to steal the task at the bottom end of the work stealing queue even if it is an affinitized to a location /// that has active searches. This is set to true on the final SFW pass to ensure a vproc does not deactivate while there /// are chores higher up in the queue that are un-affinitized and therefore inaccessible via a mailbox. /// /// /// The try lock was successfully acquired. /// /// /// An unrealized chore stolen from the work stealing queues or NULL if no such chore can be stolen /// _UnrealizedChore *TryToSteal(bool fForceStealLocalized, bool& fSuccessfullyAcquiredLock); /// /// Returns whether the unstructured work stealing queue is empty. /// bool IsUnstructuredEmpty() const { return m_unstructuredQueue.Empty(); } /// /// Sweeps the unstructured work stealing queue for items matching a predicate and potentially removes them /// based on the result of a callback. /// /// /// The predicate for things to call pSweepFn on. /// /// /// The data for the predicate and sweep callback /// /// /// The sweep function /// void SweepUnstructured(WorkStealingQueue<_UnrealizedChore>::SweepPredicate pPredicate, void *pData, WorkStealingQueue<_UnrealizedChore>::SweepFunction pSweepFn); /// /// Called in order to mark this work queue as detached so that we know how far it's legal to steal up the work /// queue should it become reattached to context with active cancellation. /// void MarkDetachment() { // // We only detach unstructured queues. // m_unstructuredQueue.MarkDetachment(); } // // Both: // /// /// Returns the id of the work queue. /// unsigned int Id() const { return m_id; } /// /// Returns whether the both work stealing queues are empty. /// bool IsEmpty() const { return m_structuredQueue.Empty() && m_unstructuredQueue.Empty(); } /// /// Sets the queue to a detached state. /// void SetDetached(bool fDetached) { if (fDetached) MarkDetachment(); InterlockedExchange(&m_detachmentState, fDetached ? QUEUE_DETACHED : QUEUE_ATTACHED); } /// /// Queries whether the queue is detached. /// bool IsDetached() const { return (m_detachmentState == QUEUE_DETACHED); } /// /// Causes a detached work queue to release its reference on the passed-in schedule group and remove itself from that schedule group's /// list of work queues at the next available safe point. /// void RetireAtSafePoint(ScheduleGroupSegmentBase *pSegment); /// /// Causes a detached work queue to redetach due to roll-back of retirement at the next available safe point. /// void RedetachFromScheduleGroupAtSafePoint(ScheduleGroupSegmentBase *pSegment); /// /// Indicates whether the steal lock is held. /// bool IsLockHeld() const { return m_lock._IsLockHeld(); } private: friend class ContextBase; friend class ScheduleGroupBase; friend class ScheduleGroupSegmentBase; template friend class ListArray; template friend void _InternalDeleteHelper(T*); // structured work stealing StructuredWorkStealingQueue<_UnrealizedChore, _CriticalNonReentrantLock> m_structuredQueue; // Intrusive links for list array. SLIST_ENTRY m_listArrayFreeLink{}; // The safe point invocation which will perform a release of schedule group held by a detached WSQ. SafePointInvocation m_detachmentSafePoint{}; // Tracking for detachment ListArrayInlineLink m_detachment; volatile long m_detachmentState; ScheduleGroupSegmentBase *m_pDetachedSegment{}; // The unique identifier for the work queue. This is the final level of binding between a task collection and a work queue. unsigned int m_id; // The index this workqueue appears at in its list array int m_listArrayIndex{}; // The context which owns the WSQ. NOTE: Any utilization of this must be capture/use as it will change outside the scope // of the WSQ list lock. ContextBase *m_pOwningContext; // Unstructured work stealing WorkStealingQueue<_UnrealizedChore, _CriticalNonReentrantLock> m_unstructuredQueue; // External lock for unstructured work stealing _CriticalNonReentrantLock m_lock; // Reinitialize a work queue pulled from a free pool void Reinitialize(); // steal helper _UnrealizedChore *UnlockedSteal(bool fForceStealLocalized); /// /// Retires the detached work queue. /// static void StaticRetire(WorkQueue *pQueue); /// /// Places the work queue back in a detached state on roll back. /// static void StaticRedetachFromScheduleGroup(WorkQueue *pQueue); }; #pragma warning(pop) } // namespace details } // namespace Concurrency