9#include <CoreFoundation/CoreFoundation.h>
14#include <shared_mutex>
97 using BufferIndex = SInt64;
101 std::optional<T> value = {};
102 std::atomic<BufferIndex> index = -1;
103 mutable std::shared_mutex mutex;
124 const auto currentIndex = doubleBuffer.currentIndex_.load();
127 auto& currentBuffer = doubleBuffer.GetBufferAt(currentIndex);
143 if (!currentBuffer.mutex.try_lock_shared()) {
152 if (currentBuffer.index.load() != currentIndex) {
153 currentBuffer.mutex.unlock_shared();
160 buffer_ = ¤tBuffer;
169 buffer_->mutex.unlock_shared();
175 : buffer_(other.buffer_)
177 other.buffer_ =
nullptr;
189 return *buffer_->value;
193 const Buffer* buffer_ =
nullptr;
205 Set(std::move(value));
239 template <
typename TT>
243 std::lock_guard writeLock(writeMutex_);
247 const auto oldIndex = currentIndex_.load(std::memory_order_relaxed);
252 const auto newIndex =
253 oldIndex < std::numeric_limits<BufferIndex>::max() ? oldIndex + 1 : 0;
256 auto& oldBuffer = GetBufferAt(oldIndex);
259 auto& newBuffer = GetBufferAt(newIndex);
264 newBuffer.index = newIndex;
275 std::unique_lock lock(newBuffer.mutex);
278 newBuffer.value = std::forward<TT>(value);
284 currentIndex_ = newIndex;
286 if constexpr (!std::is_trivial<T>::value) {
289 oldBuffer.index = -1;
293 std::unique_lock lock(oldBuffer.mutex);
298 oldBuffer.value = {};
303 friend class ReadLock;
305 const Buffer& GetBufferAt(BufferIndex index)
const
307 return buffers_[index % 2];
310 Buffer& GetBufferAt(BufferIndex index)
312 return buffers_[index % 2];
315 std::mutex writeMutex_;
318 std::atomic<BufferIndex> currentIndex_ = 0;
ReadLock(ReadLock &&other)
Move lock.
const T & GetReference() const
Get read-only reference to the value. The reference may be used until read lock destructor is called....
ReadLock(const DoubleBuffer &doubleBuffer)
Acquire the lock. Non-blocking and lock-free. May spin for a while if a setter is running concurrentl...
~ReadLock()
Release the lock.
Doubly-buffered value with non-blocking read and blocking write.
void Set(TT &&value)
Set value.
T Get() const
Get copy of the value. Non-blocking and lock-free, see ReadLock.
ReadLock GetReadLock() const
Get locked read-only reference to the value. Non-blocking and lock-free, see ReadLock.
DoubleBuffer(const T &value)
Initialize buffer with given value.
DoubleBuffer(T &&value=T())
Initialize buffer with given value.