nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: PublicHeader: None nuclear@1: Filename : OVR_ThreadCommandQueue.h nuclear@1: Content : Command queue for operations executed on a thread nuclear@1: Created : October 29, 2012 nuclear@1: Author : Michael Antonov nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: ************************************************************************************/ nuclear@1: nuclear@1: #ifndef OVR_ThreadCommandQueue_h nuclear@1: #define OVR_ThreadCommandQueue_h nuclear@1: nuclear@1: #include "Kernel/OVR_Types.h" nuclear@1: #include "Kernel/OVR_List.h" nuclear@1: #include "Kernel/OVR_Atomic.h" nuclear@1: #include "Kernel/OVR_Threads.h" nuclear@1: nuclear@1: namespace OVR { nuclear@1: nuclear@1: class ThreadCommand; nuclear@1: class ThreadCommandQueue; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** ThreadCommand nuclear@1: nuclear@1: // ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue. nuclear@1: class ThreadCommand nuclear@1: { nuclear@1: public: nuclear@1: nuclear@1: // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the nuclear@1: // calling (producer) thread when command is completed or queue slot is available. nuclear@1: class NotifyEvent : public ListNode, public NewOverrideBase nuclear@1: { nuclear@1: Event E; nuclear@1: public: nuclear@1: NotifyEvent() { } nuclear@1: nuclear@1: void Wait() { E.Wait(); } nuclear@1: void PulseEvent() { E.PulseEvent(); } nuclear@1: }; nuclear@1: nuclear@1: // ThreadCommand::PopBuffer is temporary storage for a command popped off nuclear@1: // by ThreadCommandQueue::PopCommand. nuclear@1: class PopBuffer nuclear@1: { nuclear@1: enum { MaxSize = 256 }; nuclear@1: nuclear@1: UPInt Size; nuclear@1: union { nuclear@1: UByte Buffer[MaxSize]; nuclear@1: UPInt Align; nuclear@1: }; nuclear@1: nuclear@1: ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; } nuclear@1: nuclear@1: public: nuclear@1: PopBuffer() : Size(0) { } nuclear@1: ~PopBuffer(); nuclear@1: nuclear@1: void InitFromBuffer(void* data); nuclear@1: nuclear@1: bool HasCommand() const { return Size != 0; } nuclear@1: UPInt GetSize() const { return Size; } nuclear@1: bool NeedsWait() const { return toCommand()->NeedsWait(); } nuclear@1: NotifyEvent* GetEvent() const { return toCommand()->pEvent; } nuclear@1: nuclear@1: // Execute the command and also notifies caller to finish waiting, nuclear@1: // if necessary. nuclear@1: void Execute(); nuclear@1: }; nuclear@1: nuclear@1: UInt16 Size; nuclear@1: bool WaitFlag; nuclear@1: bool ExitFlag; // Marks the last exit command. nuclear@1: NotifyEvent* pEvent; nuclear@1: nuclear@1: ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false) nuclear@1: : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { } nuclear@1: virtual ~ThreadCommand() { } nuclear@1: nuclear@1: bool NeedsWait() const { return WaitFlag; } nuclear@1: UPInt GetSize() const { return Size; } nuclear@1: nuclear@1: virtual void Execute() const = 0; nuclear@1: // Copy constructor used for serializing this to memory buffer. nuclear@1: virtual ThreadCommand* CopyConstruct(void* p) const = 0; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: // CleanType is a template that strips 'const' and '&' modifiers from the argument type; nuclear@1: // for example, typename CleanType::Type is equivalent to A. nuclear@1: template struct CleanType { typedef T Type; }; nuclear@1: template struct CleanType { typedef T Type; }; nuclear@1: template struct CleanType { typedef T Type; }; nuclear@1: template struct CleanType { typedef T Type; }; nuclear@1: nuclear@1: // SelfType is a template that yields the argument type. This helps avoid conflicts with nuclear@1: // automatic template argument deduction for function calls when identical argument nuclear@1: // is already defined. nuclear@1: template struct SelfType { typedef T Type; }; nuclear@1: nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ThreadCommand specializations for member functions with different number of nuclear@1: // arguments and argument types. nuclear@1: nuclear@1: // Used to return nothing from a ThreadCommand, to avoid problems with 'void'. nuclear@1: struct Void nuclear@1: { nuclear@1: Void() {} nuclear@1: Void(int) {} nuclear@1: }; nuclear@1: nuclear@1: // ThreadCommand for member function with 0 arguments. nuclear@1: template nuclear@1: class ThreadCommandMF0 : public ThreadCommand nuclear@1: { nuclear@1: typedef R (C::*FnPtr)(); nuclear@1: C* pClass; nuclear@1: FnPtr pFn; nuclear@1: R* pRet; nuclear@1: nuclear@1: void executeImpl() const nuclear@1: { nuclear@1: pRet ? (void)(*pRet = (pClass->*pFn)()) : nuclear@1: (void)(pClass->*pFn)(); nuclear@1: } nuclear@1: nuclear@1: public: nuclear@1: ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait) nuclear@1: : ThreadCommand(sizeof(ThreadCommandMF0), needsWait), nuclear@1: pClass(pclass), pFn(fn), pRet(ret) { } nuclear@1: nuclear@1: virtual void Execute() const { executeImpl(); } nuclear@1: virtual ThreadCommand* CopyConstruct(void* p) const nuclear@1: { return Construct(p, *this); } nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: // ThreadCommand for member function with 1 argument. nuclear@1: template nuclear@1: class ThreadCommandMF1 : public ThreadCommand nuclear@1: { nuclear@1: typedef R (C::*FnPtr)(A0); nuclear@1: C* pClass; nuclear@1: FnPtr pFn; nuclear@1: R* pRet; nuclear@1: typename CleanType::Type AVal0; nuclear@1: nuclear@1: void executeImpl() const nuclear@1: { nuclear@1: pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) : nuclear@1: (void)(pClass->*pFn)(AVal0); nuclear@1: } nuclear@1: nuclear@1: public: nuclear@1: ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait) nuclear@1: : ThreadCommand(sizeof(ThreadCommandMF1), needsWait), nuclear@1: pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { } nuclear@1: nuclear@1: virtual void Execute() const { executeImpl(); } nuclear@1: virtual ThreadCommand* CopyConstruct(void* p) const nuclear@1: { return Construct(p, *this); } nuclear@1: }; nuclear@1: nuclear@1: // ThreadCommand for member function with 2 arguments. nuclear@1: template nuclear@1: class ThreadCommandMF2 : public ThreadCommand nuclear@1: { nuclear@1: typedef R (C::*FnPtr)(A0, A1); nuclear@1: C* pClass; nuclear@1: FnPtr pFn; nuclear@1: R* pRet; nuclear@1: typename CleanType::Type AVal0; nuclear@1: typename CleanType::Type AVal1; nuclear@1: nuclear@1: void executeImpl() const nuclear@1: { nuclear@1: pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) : nuclear@1: (void)(pClass->*pFn)(AVal0, AVal1); nuclear@1: } nuclear@1: nuclear@1: public: nuclear@1: ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait) nuclear@1: : ThreadCommand(sizeof(ThreadCommandMF2), needsWait), nuclear@1: pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { } nuclear@1: nuclear@1: virtual void Execute() const { executeImpl(); } nuclear@1: virtual ThreadCommand* CopyConstruct(void* p) const nuclear@1: { return Construct(p, *this); } nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** ThreadCommandQueue nuclear@1: nuclear@1: // ThreadCommandQueue is a queue of executable function-call commands intended to be nuclear@1: // serviced by a single consumer thread. Commands are added to the queue with PushCall nuclear@1: // and removed with PopCall; they are processed in FIFO order. Multiple producer threads nuclear@1: // are supported and will be blocked if internal data buffer is full. nuclear@1: nuclear@1: class ThreadCommandQueue nuclear@1: { nuclear@1: public: nuclear@1: nuclear@1: ThreadCommandQueue(); nuclear@1: virtual ~ThreadCommandQueue(); nuclear@1: nuclear@1: nuclear@1: // Pops the next command from the thread queue, if any is available. nuclear@1: // The command should be executed by calling popBuffer->Execute(). nuclear@1: // Returns 'false' if no command is available at the time of the call. nuclear@1: bool PopCommand(ThreadCommand::PopBuffer* popBuffer); nuclear@1: nuclear@1: // Generic implementaion of PushCommand; enqueues a command for execution. nuclear@1: // Returns 'false' if push failed, usually indicating thread shutdown. nuclear@1: bool PushCommand(const ThreadCommand& command); nuclear@1: nuclear@1: // nuclear@1: void PushExitCommand(bool wait); nuclear@1: nuclear@1: // Returns 'true' once ExitCommand has been processed, so the thread can shut down. nuclear@1: bool IsExiting() const; nuclear@1: nuclear@1: nuclear@1: // These two virtual functions serve as notifications for derived nuclear@1: // thread waiting. nuclear@1: virtual void OnPushNonEmpty_Locked() { } nuclear@1: virtual void OnPopEmpty_Locked() { } nuclear@1: nuclear@1: nuclear@1: // *** PushCall with no result nuclear@1: nuclear@1: // Enqueue a member function of 'this' class to be called on consumer thread. nuclear@1: // By default the function returns immediately; set 'wait' argument to 'true' to nuclear@1: // wait for completion. nuclear@1: template nuclear@1: bool PushCall(R (C::*fn)(), bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF0(static_cast(this), fn, 0, wait)); } nuclear@1: template nuclear@1: bool PushCall(R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF1(static_cast(this), fn, 0, a0, wait)); } nuclear@1: template nuclear@1: bool PushCall(R (C::*fn)(A0, A1), nuclear@1: typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF2(static_cast(this), fn, 0, a0, a1, wait)); } nuclear@1: // Enqueue a specified member function call of class C. nuclear@1: // By default the function returns immediately; set 'wait' argument to 'true' to nuclear@1: // wait for completion. nuclear@1: template nuclear@1: bool PushCall(C* p, R (C::*fn)(), bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF0(p, fn, 0, wait)); } nuclear@1: template nuclear@1: bool PushCall(C* p, R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF1(p, fn, 0, a0, wait)); } nuclear@1: template nuclear@1: bool PushCall(C* p, R (C::*fn)(A0, A1), nuclear@1: typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) nuclear@1: { return PushCommand(ThreadCommandMF2(p, fn, 0, a0, a1, wait)); } nuclear@1: nuclear@1: nuclear@1: // *** PushCall with Result nuclear@1: nuclear@1: // Enqueue a member function of 'this' class call and wait for call to complete nuclear@1: // on consumer thread before returning. nuclear@1: template nuclear@1: bool PushCallAndWaitResult(R (C::*fn)(), R* ret) nuclear@1: { return PushCommand(ThreadCommandMF0(static_cast(this), fn, ret, true)); } nuclear@1: template nuclear@1: bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType::Type a0) nuclear@1: { return PushCommand(ThreadCommandMF1(static_cast(this), fn, ret, a0, true)); } nuclear@1: template nuclear@1: bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret, nuclear@1: typename SelfType::Type a0, typename SelfType::Type a1) nuclear@1: { return PushCommand(ThreadCommandMF2(static_cast(this), fn, ret, a0, a1, true)); } nuclear@1: // Enqueue a member function call for class C and wait for the call to complete nuclear@1: // on consumer thread before returning. nuclear@1: template nuclear@1: bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret) nuclear@1: { return PushCommand(ThreadCommandMF0(p, fn, ret, true)); } nuclear@1: template nuclear@1: bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType::Type a0) nuclear@1: { return PushCommand(ThreadCommandMF1(p, fn, ret, a0, true)); } nuclear@1: template nuclear@1: bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret, nuclear@1: typename SelfType::Type a0, typename SelfType::Type a1) nuclear@1: { return PushCommand(ThreadCommandMF2(p, fn, ret, a0, a1, true)); } nuclear@1: nuclear@1: private: nuclear@1: class ThreadCommandQueueImpl* pImpl; nuclear@1: }; nuclear@1: nuclear@1: nuclear@1: } nuclear@1: nuclear@1: #endif // OVR_ThreadCommandQueue_h