diff options
Diffstat (limited to 'LibOVRKernel/Src/Kernel/OVR_CallbacksInternal.h')
-rw-r--r-- | LibOVRKernel/Src/Kernel/OVR_CallbacksInternal.h | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/LibOVRKernel/Src/Kernel/OVR_CallbacksInternal.h b/LibOVRKernel/Src/Kernel/OVR_CallbacksInternal.h new file mode 100644 index 0000000..a0c0a31 --- /dev/null +++ b/LibOVRKernel/Src/Kernel/OVR_CallbacksInternal.h @@ -0,0 +1,331 @@ +/************************************************************************************ + +PublicHeader: Kernel +Filename : OVR_CallbacksInternal.h +Content : Callback library +Created : Nov 11, 2014 +Author : Chris Taylor + +Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.2 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#ifndef OVR_CallbacksInternal_h +#define OVR_CallbacksInternal_h + +#include "OVR_Atomic.h" +#include "OVR_RefCount.h" +#include "OVR_Delegates.h" +#include "OVR_Array.h" + +namespace OVR { + +template<class DelegateT> class FloatingCallbackEmitter; // Floating emitter object +template<class DelegateT> class CallbackEmitter; +template<class DelegateT> class FloatingCallbackListener; // Floating listener object +template<class DelegateT> class CallbackListener; + + +//----------------------------------------------------------------------------- +// FloatingCallbackEmitter +// +// The Call() function is not thread-safe. +// TBD: Should we add a thread-safe Call() option to constructor? + +class CallbackEmitterBase +{ +protected: + static Lock EmitterLock; +}; + +template<class DelegateT> +class FloatingCallbackEmitter : public CallbackEmitterBase, public RefCountBase< FloatingCallbackEmitter<DelegateT> > +{ + friend class CallbackEmitter<DelegateT>; + + FloatingCallbackEmitter() : + IsShutdown(false), + DirtyListenersCache(0) + { + } + +public: + typedef Array< Ptr< FloatingCallbackListener<DelegateT> > > ListenerPtrArray; + + ~FloatingCallbackEmitter() + { + OVR_ASSERT(Listeners.GetSizeI() == 0); + // ListenersCache will be emptied here. + } + + bool AddListener(FloatingCallbackListener<DelegateT>* listener); + void Shutdown(); + + // Called from the listener object as it is transitioning to canceled state. + // The listener's mutex is not held during this call. + void OnListenerCancel(FloatingCallbackListener<DelegateT>* listener); + +public: + void Call(); + + template<class Param1> + void Call(Param1* p1); + + template<class Param1> + void Call(Param1& p1); + + template<class Param1, class Param2> + void Call(Param1* p1, Param2* p2); + + template<class Param1, class Param2> + void Call(Param1& p1, Param2& p2); + + template<class Param1, class Param2, class Param3> + void Call(Param1* p1, Param2* p2, Param3* p3); + + template<class Param1, class Param2, class Param3> + void Call(Param1& p1, Param2& p2, Param3& p3); + +protected: + // Is the emitter shut down? This prevents more listeners from being added during shutdown. + bool IsShutdown; + + // Array of added listeners. + ListenerPtrArray Listeners; + + // Is the cache dirty? This avoids locking and memory allocation in steady state. + AtomicInt<uint32_t> DirtyListenersCache; + + // Cache of listeners used by the Call() function. + ListenerPtrArray ListenersCacheForCalls; + + // Update the ListenersCache array in response to an insertion or removal. + // This is how AddListener() insertions get rolled into the listeners array. + // This is how RemoveListener() removals get purged from the cache. + void updateListenersCache() + { + if (DirtyListenersCache != 0) + { + Lock::Locker locker(&EmitterLock); + + // TBD: Should memory allocation be further reduced here? + ListenersCacheForCalls = Listeners; + DirtyListenersCache = 0; + } + } + + // Without holding a lock, find and remove the given listener from the array of listeners. + void noLockFindAndRemoveListener(FloatingCallbackListener<DelegateT>* listener) + { + const int count = Listeners.GetSizeI(); + for (int i = 0; i < count; ++i) + { + if (Listeners[i] == listener) + { + Listeners.RemoveAt(i); + + // After removing it from the array, set the dirty flag. + // Note: Because the flag is atomic, a portable memory fence is implied. + DirtyListenersCache = 1; + + break; + } + } + } +}; + + +//----------------------------------------------------------------------------- +// FloatingCallbackListener +// +// Internal implementation class for the CallbackListener. +// This can only be associated with one CallbackListener object for its lifetime. +template<class DelegateT> +class FloatingCallbackListener : public RefCountBase< FloatingCallbackListener<DelegateT> > +{ +public: + FloatingCallbackListener(DelegateT handler); + ~FloatingCallbackListener(); + + void EnterCancelState(); + + bool IsValid() const + { + return Handler.IsValid(); + } + + // TBD: Should these be binned to reduce the lock count? + // Boost does not do that. And I am worried about deadlocks when misused. + mutable Lock ListenerLock; + + // Handler function + DelegateT Handler; +}; + + +//----------------------------------------------------------------------------- +// Template Implementation: FloatingCallbackEmitter + +template<class DelegateT> +bool FloatingCallbackEmitter<DelegateT>::AddListener(FloatingCallbackListener<DelegateT>* listener) +{ + Lock::Locker locker(&EmitterLock); + + if (IsShutdown) + { + return false; + } + + // Add the listener to our list + Listeners.PushBack(listener); + + // After adding it to the array, set the dirty flag. + // Note: Because the flag is atomic, a portable memory fence is implied. + DirtyListenersCache = 1; + + return true; +} + +// Called from the listener object as it is transitioning to canceled state. +// The listener's mutex is not held during this call. +template<class DelegateT> +void FloatingCallbackEmitter<DelegateT>::OnListenerCancel(FloatingCallbackListener<DelegateT>* listener) +{ + Lock::Locker emitterLocker(&EmitterLock); + + // If not shut down, + // Note that if it is shut down then there will be no listeners in the array. + if (!IsShutdown) + { + // Remove it. + noLockFindAndRemoveListener(listener); + } +} + +template<class DelegateT> +void FloatingCallbackEmitter<DelegateT>::Shutdown() +{ + Lock::Locker locker(&EmitterLock); + + IsShutdown = true; + + Listeners.ClearAndRelease(); + + // Note: Because the flag is atomic, a portable memory fence is implied. + DirtyListenersCache = 1; +} + +//----------------------------------------------------------------------------- +// Call function +// +// (1) Update the cache of listener references, if it has changed. +// (2) For each listener, +// (a) Hold ListenerLock. +// (b) If listener handler is valid, call the handler. +#define OVR_EMITTER_CALL_BODY(params) \ + updateListenersCache(); \ + if (IsShutdown) return; /* Pure optimization. It is fine if this races. */ \ + const int count = ListenersCacheForCalls.GetSizeI(); \ + for (int i = 0; i < count; ++i) \ + { \ + Lock::Locker locker(&ListenersCacheForCalls[i]->ListenerLock); \ + if (ListenersCacheForCalls[i]->Handler.IsValid()) \ + { \ + ListenersCacheForCalls[i]->Handler params; /* Using a macro for this line. */ \ + } \ + } + +template<class DelegateT> +void FloatingCallbackEmitter<DelegateT>::Call() +{ + OVR_EMITTER_CALL_BODY(()) +} + +template<class DelegateT> +template<class Param1> +void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1) +{ + OVR_EMITTER_CALL_BODY((p1)) +} + +template<class DelegateT> +template<class Param1> +void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1) +{ + OVR_EMITTER_CALL_BODY((p1)) +} + +template<class DelegateT> +template<class Param1, class Param2> +void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1, Param2* p2) +{ + OVR_EMITTER_CALL_BODY((p1, p2)) +} + +template<class DelegateT> +template<class Param1, class Param2> +void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1, Param2& p2) +{ + OVR_EMITTER_CALL_BODY((p1, p2)) +} + +template<class DelegateT> +template<class Param1, class Param2, class Param3> +void FloatingCallbackEmitter<DelegateT>::Call(Param1* p1, Param2* p2, Param3* p3) +{ + OVR_EMITTER_CALL_BODY((p1, p2, p3)) +} + +template<class DelegateT> +template<class Param1, class Param2, class Param3> +void FloatingCallbackEmitter<DelegateT>::Call(Param1& p1, Param2& p2, Param3& p3) +{ + OVR_EMITTER_CALL_BODY((p1, p2, p3)) +} + +#undef OVR_EMITTER_CALL_BODY + + +//----------------------------------------------------------------------------- +// Template Implementation: FloatingCallbackListener + +template<class DelegateT> +FloatingCallbackListener<DelegateT>::FloatingCallbackListener(DelegateT handler) : + Handler(handler) +{ + OVR_ASSERT(Handler.IsValid()); +} + +template<class DelegateT> +FloatingCallbackListener<DelegateT>::~FloatingCallbackListener() +{ + OVR_ASSERT(!Handler.IsValid()); +} + +template<class DelegateT> +void FloatingCallbackListener<DelegateT>::EnterCancelState() +{ + ListenerLock.DoLock(); + Handler.Invalidate(); + ListenerLock.Unlock(); +} + + +} // namespace OVR + +#endif // OVR_CallbacksInternal_h |