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