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