OpQueue.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. // OpQueue.h
  4. // Async operation queue.
  5. //
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  7. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  8. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  9. // PARTICULAR PURPOSE.
  10. //
  11. // Copyright (c) Microsoft Corporation. All rights reserved.
  12. //
  13. //////////////////////////////////////////////////////////////////////////
  14. #pragma once
  15. #pragma warning( push )
  16. #pragma warning( disable : 4355 ) // 'this' used in base member initializer list
  17. /*
  18. This header file defines an object to help queue and serialize
  19. asynchronous operations.
  20. Background:
  21. To perform an operation asynchronously in Media Foundation, an object
  22. does one of the following:
  23. 1. Calls MFPutWorkItem(Ex), using either a standard work queue
  24. identifier or a caller-allocated work queue. The work-queue
  25. thread invokes the object's callback.
  26. 2. Creates an async result object (IMFAsyncResult) and calls
  27. MFInvokeCallback to invoke the object's callback.
  28. Ultimately, either of these cause the object's callback to be invoked
  29. from a work-queue thread. The object can then complete the operation
  30. inside the callback.
  31. However, the Media Foundation platform may dispatch async callbacks in
  32. parallel on several threads. Putting an item on a work queue does NOT
  33. guarantee that one operation will complete before the next one starts,
  34. or even that work items will be dispatched in the same order they were
  35. called.
  36. To serialize async operations that should not overlap, an object should
  37. use a queue. While one operation is pending, subsequent operations are
  38. put on the queue, and only dispatched after the previous operation is
  39. complete.
  40. The granularity of a single "operation" depends on the requirements of
  41. that particular object. A single operation might involve several
  42. asynchronous calls before the object dispatches the next operation on
  43. the queue.
  44. */
  45. //-------------------------------------------------------------------
  46. // OpQueue class template
  47. //
  48. // Base class for an async operation queue.
  49. //
  50. // TOperation: The class used to describe operations. This class must
  51. // implement IUnknown.
  52. //
  53. // The OpQueue class is an abstract class. The derived class must
  54. // implement the following pure-virtual methods:
  55. //
  56. // - IUnknown methods (AddRef, Release, QI)
  57. //
  58. // - DispatchOperation:
  59. //
  60. // Performs the asynchronous operation specified by pOp.
  61. //
  62. // At the end of each operation, the derived class must call
  63. // ProcessQueue to process the next operation in the queue.
  64. //
  65. // NOTE: An operation is not required to complete inside the
  66. // DispatchOperation method. A single operation might consist
  67. // of several asynchronous method calls.
  68. //
  69. // - ValidateOperation:
  70. //
  71. // Checks whether the object can perform the operation specified
  72. // by pOp at this time.
  73. //
  74. // If the object cannot perform the operation now (e.g., because
  75. // another operation is still in progress) the method should
  76. // return MF_E_NOTACCEPTING.
  77. //
  78. //-------------------------------------------------------------------
  79. #include "linklist.h"
  80. #include "AsyncCB.h"
  81. template <class T, class TOperation>
  82. class OpQueue //: public IUnknown
  83. {
  84. public:
  85. typedef ComPtrList<TOperation> OpList;
  86. HRESULT QueueOperation(TOperation *pOp);
  87. protected:
  88. HRESULT ProcessQueue();
  89. HRESULT ProcessQueueAsync(IMFAsyncResult *pResult);
  90. virtual HRESULT DispatchOperation(TOperation *pOp) = 0;
  91. virtual HRESULT ValidateOperation(TOperation *pOp) = 0;
  92. OpQueue(CRITICAL_SECTION& critsec)
  93. : m_OnProcessQueue(static_cast<T *>(this), &OpQueue::ProcessQueueAsync),
  94. m_critsec(critsec)
  95. {
  96. }
  97. virtual ~OpQueue()
  98. {
  99. }
  100. protected:
  101. OpList m_OpQueue; // Queue of operations.
  102. CRITICAL_SECTION& m_critsec; // Protects the queue state.
  103. AsyncCallback<T> m_OnProcessQueue; // ProcessQueueAsync callback.
  104. };
  105. //-------------------------------------------------------------------
  106. // Place an operation on the queue.
  107. // Public method.
  108. //-------------------------------------------------------------------
  109. template <class T, class TOperation>
  110. HRESULT OpQueue<T, TOperation>::QueueOperation(TOperation *pOp)
  111. {
  112. HRESULT hr = S_OK;
  113. EnterCriticalSection(&m_critsec);
  114. hr = m_OpQueue.InsertBack(pOp);
  115. if (SUCCEEDED(hr))
  116. {
  117. hr = ProcessQueue();
  118. }
  119. LeaveCriticalSection(&m_critsec);
  120. return hr;
  121. }
  122. //-------------------------------------------------------------------
  123. // Process the next operation on the queue.
  124. // Protected method.
  125. //
  126. // Note: This method dispatches the operation to a work queue.
  127. //-------------------------------------------------------------------
  128. template <class T, class TOperation>
  129. HRESULT OpQueue<T, TOperation>::ProcessQueue()
  130. {
  131. HRESULT hr = S_OK;
  132. if (m_OpQueue.GetCount() > 0)
  133. {
  134. hr = MFPutWorkItem2(
  135. MFASYNC_CALLBACK_QUEUE_STANDARD, // Use the standard work queue.
  136. 0, // Default priority
  137. &m_OnProcessQueue, // Callback method.
  138. nullptr // State object.
  139. );
  140. }
  141. return hr;
  142. }
  143. //-------------------------------------------------------------------
  144. // Process the next operation on the queue.
  145. // Protected method.
  146. //
  147. // Note: This method is called from a work-queue thread.
  148. //-------------------------------------------------------------------
  149. template <class T, class TOperation>
  150. HRESULT OpQueue<T, TOperation>::ProcessQueueAsync(IMFAsyncResult *pResult)
  151. {
  152. HRESULT hr = S_OK;
  153. TOperation *pOp = nullptr;
  154. EnterCriticalSection(&m_critsec);
  155. if (m_OpQueue.GetCount() > 0)
  156. {
  157. hr = m_OpQueue.GetFront(&pOp);
  158. if (SUCCEEDED(hr))
  159. {
  160. hr = ValidateOperation(pOp);
  161. }
  162. if (SUCCEEDED(hr))
  163. {
  164. hr = m_OpQueue.RemoveFront(nullptr);
  165. }
  166. if (SUCCEEDED(hr))
  167. {
  168. (void)DispatchOperation(pOp);
  169. }
  170. }
  171. if (pOp != nullptr)
  172. {
  173. pOp->Release();
  174. }
  175. LeaveCriticalSection(&m_critsec);
  176. return hr;
  177. }
  178. #pragma warning( pop )