oculus1

annotate libovr/Src/OVR_ThreadCommandQueue.h @ 17:cfe4979ab3eb

ops, minor error in the last commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 21 Sep 2013 07:09:48 +0300
parents
children
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