diff options
Diffstat (limited to 'ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Lockless.h')
-rw-r--r-- | ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Lockless.h | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Lockless.h b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Lockless.h new file mode 100644 index 0000000..a3fbac5 --- /dev/null +++ b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Kernel/OVR_Lockless.h @@ -0,0 +1,202 @@ +/************************************************************************************ + +Filename : OVR_Lockless.h +Content : Lock-less classes for producer/consumer communication +Created : November 9, 2013 +Authors : John Carmack + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +Licensed under the Oculus Master SDK License Version 1.0 (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 + +https://developer.oculus.com/licenses/oculusmastersdk-1.0 + +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. + +template <class T, class SlotType = T> +class LocklessUpdater { + public: + LocklessUpdater() { + 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(std::memory_order_acquire); + state = Slots[end & 1]; + begin = UpdateBegin.load(std::memory_order_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(std::memory_order_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.fetch_add(1) & 1; + // Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add. + Slots[slot ^ 1] = state; + UpdateEnd.fetch_add(1); + } + + std::atomic<int> UpdateBegin = {0}; + std::atomic<int> UpdateEnd = {0}; + 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) + +// FIXME: Move this somewhere else + +// ***** LocklessBuffer + +// FIXME: update these comments +// For single producer cases where you only care about the most recent update, not +// necessarily getting every one that happens (vsync timing, SensorFusion updates, external camera +// frames). +// +// The writer writes an incrementing generation # for each write start, and write end +// The reader reads the last written generation number, saves it, does its operations, then +// reads the latest value of the last written generation number. If they match, there was no +// collision, and the work is done. If not, the reader has to loop until it gets a matching +// Last written generation number +// +// This is to update & read a dynamically sized object in shared memory. +// Initial use case is for frame buffers for cameras, which are an unknown size until runtime + +#pragma pack(push, 1) + +class LocklessBuffer { + public: + LocklessBuffer() { + ; + } + + void Initialize(unsigned bufferSize) { + BufferSize = bufferSize; + LastWrittenGeneration = 0; + } + + char* StartWrite(unsigned offset) { + ++LastWrittenGeneration; + return GetDataForWrite(offset); + } + + unsigned GetBufferSize() const { + return BufferSize; + } + + int EndWrite() { + return ++LastWrittenGeneration; + } + + int GetLastWrittenGeneration() const { + return LastWrittenGeneration; + } + + const char* GetDataForRead(unsigned offset = 0) const { + return &(Data[offset]); + } + char* GetDataForWrite(unsigned offset = 0) { + return &(Data[offset]); + } + + bool DidReadCollide(int lastReadGeneration) const { + return lastReadGeneration != LastWrittenGeneration; + } + + private: + unsigned BufferSize = 0; + std::atomic<int> LastWrittenGeneration; + + // Data starts here... + char Data[1]; +}; + +#pragma pack(pop) + +} // namespace OVR + +#endif // OVR_Lockless_h |