nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: OVR_Kernel.h nuclear@0: Filename : OVR_Lockless.h nuclear@0: Content : Lock-less classes for producer/consumer communication nuclear@0: Created : November 9, 2013 nuclear@0: Authors : John Carmack 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_Lockless_h nuclear@0: #define OVR_Lockless_h nuclear@0: nuclear@0: #include "OVR_Atomic.h" nuclear@0: nuclear@0: // Define this to compile-in Lockless test logic nuclear@0: //#define OVR_LOCKLESS_TEST nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: nuclear@0: // ***** LocklessUpdater nuclear@0: nuclear@0: // For single producer cases where you only care about the most recent update, not nuclear@0: // necessarily getting every one that happens (vsync timing, SensorFusion updates). nuclear@0: // nuclear@0: // This is multiple consumer safe, but is currently only used with a single consumer. nuclear@0: // nuclear@0: // The SlotType can be the same as T, but should probably be a larger fixed size. nuclear@0: // This allows for forward compatibility when the updater is shared between processes. nuclear@0: nuclear@0: // FIXME: ExchangeAdd_Sync() should be replaced with a portable read-only primitive, nuclear@0: // so that the lockless pose state can be read-only on remote processes and to reduce nuclear@0: // false sharing between processes and improve performance. nuclear@0: nuclear@0: template nuclear@0: class LocklessUpdater nuclear@0: { nuclear@0: public: nuclear@0: LocklessUpdater() : UpdateBegin( 0 ), UpdateEnd( 0 ) nuclear@0: { nuclear@0: OVR_COMPILER_ASSERT(sizeof(T) <= sizeof(SlotType)); nuclear@0: } nuclear@0: nuclear@0: T GetState() const nuclear@0: { nuclear@0: // Copy the state out, then retry with the alternate slot nuclear@0: // if we determine that our copy may have been partially nuclear@0: // stepped on by a new update. nuclear@0: T state; nuclear@0: int begin, end, final; nuclear@0: nuclear@0: for(;;) nuclear@0: { nuclear@0: // We are adding 0, only using these as atomic memory barriers, so it nuclear@0: // is ok to cast off the const, allowing GetState() to remain const. nuclear@0: end = UpdateEnd.Load_Acquire(); nuclear@0: state = Slots[ end & 1 ]; nuclear@0: begin = UpdateBegin.Load_Acquire(); nuclear@0: if ( begin == end ) { nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: // The producer is potentially blocked while only having partially nuclear@0: // written the update, so copy out the other slot. nuclear@0: state = Slots[ (begin & 1) ^ 1 ]; nuclear@0: final = UpdateBegin.Load_Acquire(); nuclear@0: if ( final == begin ) { nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: // The producer completed the last update and started a new one before nuclear@0: // we got it copied out, so try fetching the current buffer again. nuclear@0: } nuclear@0: return state; nuclear@0: } nuclear@0: nuclear@0: void SetState( const T& state ) nuclear@0: { nuclear@0: const int slot = UpdateBegin.ExchangeAdd_Sync(1) & 1; nuclear@0: // Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add. nuclear@0: Slots[slot ^ 1] = state; nuclear@0: UpdateEnd.ExchangeAdd_Sync(1); nuclear@0: } nuclear@0: nuclear@0: AtomicInt UpdateBegin; nuclear@0: AtomicInt UpdateEnd; nuclear@0: SlotType Slots[2]; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: #ifdef OVR_LOCKLESS_TEST nuclear@0: void StartLocklessTest(); nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: } // namespace OVR nuclear@0: nuclear@0: #endif // OVR_Lockless_h nuclear@0: