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