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