rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 PublicHeader: None
|
nuclear@0
|
4 Filename : OVR_ThreadCommandQueue.h
|
nuclear@0
|
5 Content : Command queue for operations executed on a thread
|
nuclear@0
|
6 Created : October 29, 2012
|
nuclear@0
|
7 Author : Michael Antonov
|
nuclear@0
|
8
|
nuclear@0
|
9 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
|
nuclear@0
|
10
|
nuclear@0
|
11 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
|
nuclear@0
|
12 you may not use the Oculus VR Rift SDK except in compliance with the License,
|
nuclear@0
|
13 which is provided at the time of installation or download, or which
|
nuclear@0
|
14 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@0
|
15
|
nuclear@0
|
16 You may obtain a copy of the License at
|
nuclear@0
|
17
|
nuclear@0
|
18 http://www.oculusvr.com/licenses/LICENSE-3.2
|
nuclear@0
|
19
|
nuclear@0
|
20 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
nuclear@0
|
21 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
23 See the License for the specific language governing permissions and
|
nuclear@0
|
24 limitations under the License.
|
nuclear@0
|
25
|
nuclear@0
|
26 ************************************************************************************/
|
nuclear@0
|
27
|
nuclear@0
|
28 #ifndef OVR_ThreadCommandQueue_h
|
nuclear@0
|
29 #define OVR_ThreadCommandQueue_h
|
nuclear@0
|
30
|
nuclear@0
|
31 #include "../Kernel/OVR_Types.h"
|
nuclear@0
|
32 #include "../Kernel/OVR_List.h"
|
nuclear@0
|
33 #include "../Kernel/OVR_Atomic.h"
|
nuclear@0
|
34 #include "../Kernel/OVR_Threads.h"
|
nuclear@0
|
35
|
nuclear@0
|
36 namespace OVR {
|
nuclear@0
|
37
|
nuclear@0
|
38 class ThreadCommand;
|
nuclear@0
|
39 class ThreadCommandQueue;
|
nuclear@0
|
40
|
nuclear@0
|
41
|
nuclear@0
|
42 //-------------------------------------------------------------------------------------
|
nuclear@0
|
43 // ***** ThreadCommand
|
nuclear@0
|
44
|
nuclear@0
|
45 // ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue.
|
nuclear@0
|
46 class ThreadCommand
|
nuclear@0
|
47 {
|
nuclear@0
|
48 public:
|
nuclear@0
|
49 // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the
|
nuclear@0
|
50 // calling (producer) thread when command is completed or queue slot is available.
|
nuclear@0
|
51 class NotifyEvent : public ListNode<NotifyEvent>, public NewOverrideBase
|
nuclear@0
|
52 {
|
nuclear@0
|
53 Event E;
|
nuclear@0
|
54 public:
|
nuclear@0
|
55 NotifyEvent() { }
|
nuclear@0
|
56
|
nuclear@0
|
57 void Wait() { E.Wait(); }
|
nuclear@0
|
58 void PulseEvent() { E.PulseEvent(); }
|
nuclear@0
|
59 };
|
nuclear@0
|
60
|
nuclear@0
|
61 // ThreadCommand::PopBuffer is temporary storage for a command popped off
|
nuclear@0
|
62 // by ThreadCommandQueue::PopCommand.
|
nuclear@0
|
63 class PopBuffer
|
nuclear@0
|
64 {
|
nuclear@0
|
65 enum { MaxSize = 256 };
|
nuclear@0
|
66
|
nuclear@0
|
67 size_t Size;
|
nuclear@0
|
68 union {
|
nuclear@0
|
69 uint8_t Buffer[MaxSize];
|
nuclear@0
|
70 size_t Align;
|
nuclear@0
|
71 };
|
nuclear@0
|
72
|
nuclear@0
|
73 ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; }
|
nuclear@0
|
74
|
nuclear@0
|
75 public:
|
nuclear@0
|
76 PopBuffer() : Size(0) { }
|
nuclear@0
|
77 ~PopBuffer();
|
nuclear@0
|
78
|
nuclear@0
|
79 void InitFromBuffer(void* data);
|
nuclear@0
|
80
|
nuclear@0
|
81 bool HasCommand() const { return Size != 0; }
|
nuclear@0
|
82 size_t GetSize() const { return Size; }
|
nuclear@0
|
83 bool NeedsWait() const { return toCommand()->NeedsWait(); }
|
nuclear@0
|
84 NotifyEvent* GetEvent() const { return toCommand()->pEvent; }
|
nuclear@0
|
85
|
nuclear@0
|
86 // Execute the command and also notifies caller to finish waiting,
|
nuclear@0
|
87 // if necessary.
|
nuclear@0
|
88 void Execute();
|
nuclear@0
|
89 };
|
nuclear@0
|
90
|
nuclear@0
|
91 uint16_t Size;
|
nuclear@0
|
92 bool WaitFlag;
|
nuclear@0
|
93 bool ExitFlag; // Marks the last exit command.
|
nuclear@0
|
94 NotifyEvent* pEvent;
|
nuclear@0
|
95
|
nuclear@0
|
96 ThreadCommand(size_t size, bool waitFlag, bool exitFlag = false)
|
nuclear@0
|
97 : Size((uint16_t)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { }
|
nuclear@0
|
98 virtual ~ThreadCommand() { }
|
nuclear@0
|
99
|
nuclear@0
|
100 bool NeedsWait() const { return WaitFlag; }
|
nuclear@0
|
101 size_t GetSize() const { return Size; }
|
nuclear@0
|
102
|
nuclear@0
|
103 virtual void Execute() const = 0;
|
nuclear@0
|
104 // Copy constructor used for serializing this to memory buffer.
|
nuclear@0
|
105 virtual ThreadCommand* CopyConstruct(void* p) const = 0;
|
nuclear@0
|
106 };
|
nuclear@0
|
107
|
nuclear@0
|
108
|
nuclear@0
|
109 //-------------------------------------------------------------------------------------
|
nuclear@0
|
110
|
nuclear@0
|
111 // CleanType is a template that strips 'const' and '&' modifiers from the argument type;
|
nuclear@0
|
112 // for example, typename CleanType<A&>::Type is equivalent to A.
|
nuclear@0
|
113 template<class T> struct CleanType { typedef T Type; };
|
nuclear@0
|
114 template<class T> struct CleanType<T&> { typedef T Type; };
|
nuclear@0
|
115 template<class T> struct CleanType<const T> { typedef T Type; };
|
nuclear@0
|
116 template<class T> struct CleanType<const T&> { typedef T Type; };
|
nuclear@0
|
117
|
nuclear@0
|
118 // SelfType is a template that yields the argument type. This helps avoid conflicts with
|
nuclear@0
|
119 // automatic template argument deduction for function calls when identical argument
|
nuclear@0
|
120 // is already defined.
|
nuclear@0
|
121 template<class T> struct SelfType { typedef T Type; };
|
nuclear@0
|
122
|
nuclear@0
|
123
|
nuclear@0
|
124
|
nuclear@0
|
125 //-------------------------------------------------------------------------------------
|
nuclear@0
|
126 // ThreadCommand specializations for member functions with different number of
|
nuclear@0
|
127 // arguments and argument types.
|
nuclear@0
|
128
|
nuclear@0
|
129 // Used to return nothing from a ThreadCommand, to avoid problems with 'void'.
|
nuclear@0
|
130 struct Void
|
nuclear@0
|
131 {
|
nuclear@0
|
132 Void() {}
|
nuclear@0
|
133 Void(int) {}
|
nuclear@0
|
134 };
|
nuclear@0
|
135
|
nuclear@0
|
136 // ThreadCommand for member function with 0 arguments.
|
nuclear@0
|
137 template<class C, class R>
|
nuclear@0
|
138 class ThreadCommandMF0 : public ThreadCommand
|
nuclear@0
|
139 {
|
nuclear@0
|
140 typedef R (C::*FnPtr)();
|
nuclear@0
|
141 C* pClass;
|
nuclear@0
|
142 FnPtr pFn;
|
nuclear@0
|
143 R* pRet;
|
nuclear@0
|
144
|
nuclear@0
|
145 void executeImpl() const
|
nuclear@0
|
146 {
|
nuclear@0
|
147 pRet ? (void)(*pRet = (pClass->*pFn)()) :
|
nuclear@0
|
148 (void)(pClass->*pFn)();
|
nuclear@0
|
149 }
|
nuclear@0
|
150
|
nuclear@0
|
151 public:
|
nuclear@0
|
152 ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait)
|
nuclear@0
|
153 : ThreadCommand(sizeof(ThreadCommandMF0), needsWait),
|
nuclear@0
|
154 pClass(pclass), pFn(fn), pRet(ret) { }
|
nuclear@0
|
155
|
nuclear@0
|
156 virtual void Execute() const { executeImpl(); }
|
nuclear@0
|
157 virtual ThreadCommand* CopyConstruct(void* p) const
|
nuclear@0
|
158 { return Construct<ThreadCommandMF0>(p, *this); }
|
nuclear@0
|
159 };
|
nuclear@0
|
160
|
nuclear@0
|
161
|
nuclear@0
|
162 // ThreadCommand for member function with 1 argument.
|
nuclear@0
|
163 template<class C, class R, class A0>
|
nuclear@0
|
164 class ThreadCommandMF1 : public ThreadCommand
|
nuclear@0
|
165 {
|
nuclear@0
|
166 typedef R (C::*FnPtr)(A0);
|
nuclear@0
|
167 C* pClass;
|
nuclear@0
|
168 FnPtr pFn;
|
nuclear@0
|
169 R* pRet;
|
nuclear@0
|
170 typename CleanType<A0>::Type AVal0;
|
nuclear@0
|
171
|
nuclear@0
|
172 void executeImpl() const
|
nuclear@0
|
173 {
|
nuclear@0
|
174 pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) :
|
nuclear@0
|
175 (void)(pClass->*pFn)(AVal0);
|
nuclear@0
|
176 }
|
nuclear@0
|
177
|
nuclear@0
|
178 public:
|
nuclear@0
|
179 ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait)
|
nuclear@0
|
180 : ThreadCommand(sizeof(ThreadCommandMF1), needsWait),
|
nuclear@0
|
181 pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { }
|
nuclear@0
|
182
|
nuclear@0
|
183 virtual void Execute() const { executeImpl(); }
|
nuclear@0
|
184 virtual ThreadCommand* CopyConstruct(void* p) const
|
nuclear@0
|
185 { return Construct<ThreadCommandMF1>(p, *this); }
|
nuclear@0
|
186 };
|
nuclear@0
|
187
|
nuclear@0
|
188 // ThreadCommand for member function with 2 arguments.
|
nuclear@0
|
189 template<class C, class R, class A0, class A1>
|
nuclear@0
|
190 class ThreadCommandMF2 : public ThreadCommand
|
nuclear@0
|
191 {
|
nuclear@0
|
192 typedef R (C::*FnPtr)(A0, A1);
|
nuclear@0
|
193 C* pClass;
|
nuclear@0
|
194 FnPtr pFn;
|
nuclear@0
|
195 R* pRet;
|
nuclear@0
|
196 typename CleanType<A0>::Type AVal0;
|
nuclear@0
|
197 typename CleanType<A1>::Type AVal1;
|
nuclear@0
|
198
|
nuclear@0
|
199 void executeImpl() const
|
nuclear@0
|
200 {
|
nuclear@0
|
201 pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) :
|
nuclear@0
|
202 (void)(pClass->*pFn)(AVal0, AVal1);
|
nuclear@0
|
203 }
|
nuclear@0
|
204
|
nuclear@0
|
205 public:
|
nuclear@0
|
206 ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait)
|
nuclear@0
|
207 : ThreadCommand(sizeof(ThreadCommandMF2), needsWait),
|
nuclear@0
|
208 pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { }
|
nuclear@0
|
209
|
nuclear@0
|
210 virtual void Execute() const { executeImpl(); }
|
nuclear@0
|
211 virtual ThreadCommand* CopyConstruct(void* p) const
|
nuclear@0
|
212 { return Construct<ThreadCommandMF2>(p, *this); }
|
nuclear@0
|
213 };
|
nuclear@0
|
214
|
nuclear@0
|
215
|
nuclear@0
|
216 //-------------------------------------------------------------------------------------
|
nuclear@0
|
217 // ***** ThreadCommandQueue
|
nuclear@0
|
218
|
nuclear@0
|
219 // ThreadCommandQueue is a queue of executable function-call commands intended to be
|
nuclear@0
|
220 // serviced by a single consumer thread. Commands are added to the queue with PushCall
|
nuclear@0
|
221 // and removed with PopCall; they are processed in FIFO order. Multiple producer threads
|
nuclear@0
|
222 // are supported and will be blocked if internal data buffer is full.
|
nuclear@0
|
223
|
nuclear@0
|
224 class ThreadCommandQueue
|
nuclear@0
|
225 {
|
nuclear@0
|
226 public:
|
nuclear@0
|
227
|
nuclear@0
|
228 ThreadCommandQueue();
|
nuclear@0
|
229 virtual ~ThreadCommandQueue();
|
nuclear@0
|
230
|
nuclear@0
|
231
|
nuclear@0
|
232 // Pops the next command from the thread queue, if any is available.
|
nuclear@0
|
233 // The command should be executed by calling popBuffer->Execute().
|
nuclear@0
|
234 // Returns 'false' if no command is available at the time of the call.
|
nuclear@0
|
235 bool PopCommand(ThreadCommand::PopBuffer* popBuffer);
|
nuclear@0
|
236
|
nuclear@0
|
237 // Generic implementaion of PushCommand; enqueues a command for execution.
|
nuclear@0
|
238 // Returns 'false' if push failed, usually indicating thread shutdown.
|
nuclear@0
|
239 bool PushCommand(const ThreadCommand& command);
|
nuclear@0
|
240
|
nuclear@0
|
241 //
|
nuclear@0
|
242 void PushExitCommand(bool wait);
|
nuclear@0
|
243
|
nuclear@0
|
244 // Returns 'true' once ExitCommand has been processed, so the thread can shut down.
|
nuclear@0
|
245 bool IsExiting() const;
|
nuclear@0
|
246
|
nuclear@0
|
247
|
nuclear@0
|
248 // These two virtual functions serve as notifications for derived
|
nuclear@0
|
249 // thread waiting.
|
nuclear@0
|
250 virtual void OnPushNonEmpty_Locked() { }
|
nuclear@0
|
251 virtual void OnPopEmpty_Locked() { }
|
nuclear@0
|
252
|
nuclear@0
|
253
|
nuclear@0
|
254 // *** PushCall with no result
|
nuclear@0
|
255
|
nuclear@0
|
256 // Enqueue a member function of 'this' class to be called on consumer thread.
|
nuclear@0
|
257 // By default the function returns immediately; set 'wait' argument to 'true' to
|
nuclear@0
|
258 // wait for completion.
|
nuclear@0
|
259 template<class C, class R>
|
nuclear@0
|
260 bool PushCall(R (C::*fn)(), bool wait = false)
|
nuclear@0
|
261 { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, 0, wait)); }
|
nuclear@0
|
262 template<class C, class R, class A0>
|
nuclear@0
|
263 bool PushCall(R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
|
nuclear@0
|
264 { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, 0, a0, wait)); }
|
nuclear@0
|
265 template<class C, class R, class A0, class A1>
|
nuclear@0
|
266 bool PushCall(R (C::*fn)(A0, A1),
|
nuclear@0
|
267 typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
|
nuclear@0
|
268 { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, 0, a0, a1, wait)); }
|
nuclear@0
|
269 // Enqueue a specified member function call of class C.
|
nuclear@0
|
270 // By default the function returns immediately; set 'wait' argument to 'true' to
|
nuclear@0
|
271 // wait for completion.
|
nuclear@0
|
272 template<class C, class R>
|
nuclear@0
|
273 bool PushCall(C* p, R (C::*fn)(), bool wait = false)
|
nuclear@0
|
274 { return PushCommand(ThreadCommandMF0<C,R>(p, fn, 0, wait)); }
|
nuclear@0
|
275 template<class C, class R, class A0>
|
nuclear@0
|
276 bool PushCall(C* p, R (C::*fn)(A0), typename SelfType<A0>::Type a0, bool wait = false)
|
nuclear@0
|
277 { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, 0, a0, wait)); }
|
nuclear@0
|
278 template<class C, class R, class A0, class A1>
|
nuclear@0
|
279 bool PushCall(C* p, R (C::*fn)(A0, A1),
|
nuclear@0
|
280 typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1, bool wait = false)
|
nuclear@0
|
281 { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, 0, a0, a1, wait)); }
|
nuclear@0
|
282
|
nuclear@0
|
283
|
nuclear@0
|
284 // *** PushCall with Result
|
nuclear@0
|
285
|
nuclear@0
|
286 // Enqueue a member function of 'this' class call and wait for call to complete
|
nuclear@0
|
287 // on consumer thread before returning.
|
nuclear@0
|
288 template<class C, class R>
|
nuclear@0
|
289 bool PushCallAndWaitResult(R (C::*fn)(), R* ret)
|
nuclear@0
|
290 { return PushCommand(ThreadCommandMF0<C,R>(static_cast<C*>(this), fn, ret, true)); }
|
nuclear@0
|
291 template<class C, class R, class A0>
|
nuclear@0
|
292 bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
|
nuclear@0
|
293 { return PushCommand(ThreadCommandMF1<C,R,A0>(static_cast<C*>(this), fn, ret, a0, true)); }
|
nuclear@0
|
294 template<class C, class R, class A0, class A1>
|
nuclear@0
|
295 bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret,
|
nuclear@0
|
296 typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
|
nuclear@0
|
297 { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(static_cast<C*>(this), fn, ret, a0, a1, true)); }
|
nuclear@0
|
298 // Enqueue a member function call for class C and wait for the call to complete
|
nuclear@0
|
299 // on consumer thread before returning.
|
nuclear@0
|
300 template<class C, class R>
|
nuclear@0
|
301 bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret)
|
nuclear@0
|
302 { return PushCommand(ThreadCommandMF0<C,R>(p, fn, ret, true)); }
|
nuclear@0
|
303 template<class C, class R, class A0>
|
nuclear@0
|
304 bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType<A0>::Type a0)
|
nuclear@0
|
305 { return PushCommand(ThreadCommandMF1<C,R,A0>(p, fn, ret, a0, true)); }
|
nuclear@0
|
306 template<class C, class R, class A0, class A1>
|
nuclear@0
|
307 bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret,
|
nuclear@0
|
308 typename SelfType<A0>::Type a0, typename SelfType<A1>::Type a1)
|
nuclear@0
|
309 { return PushCommand(ThreadCommandMF2<C,R,A0,A1>(p, fn, ret, a0, a1, true)); }
|
nuclear@0
|
310
|
nuclear@0
|
311 private:
|
nuclear@0
|
312 class ThreadCommandQueueImpl* pImpl;
|
nuclear@0
|
313 };
|
nuclear@0
|
314
|
nuclear@0
|
315
|
nuclear@0
|
316 } // namespace OVR
|
nuclear@0
|
317
|
nuclear@0
|
318 #endif // OVR_ThreadCommandQueue_h
|