#pragma once #if !defined(MIJIN_MESSAGE_QUEUE_HPP_INCLUDED) #define MIJIN_MESSAGE_QUEUE_HPP_INCLUDED 1 #include #include #include #include #include "../util/bitarray.hpp" namespace mijin { // // public defines // // // public constants // // // public types // template class MessageQueue { private: std::array messages; mijin::BitArray messageReady; std::atomic_uint writePos = 0; std::atomic_uint readPos = 0; public: MessageQueue() = default; MessageQueue(const MessageQueue&) = delete; MessageQueue(MessageQueue&&) noexcept = delete; MessageQueue& operator=(const MessageQueue&) = delete; MessageQueue& operator=(MessageQueue&&) noexcept = delete; [[nodiscard]] bool tryPushMaybeMove(TMessage& message); [[nodiscard]] bool tryPush(TMessage message) { return tryPushMaybeMove(message); } void push(TMessage message); [[nodiscard]] std::optional tryPop(); [[nodiscard]] TMessage wait(); }; template struct TaskMessageQueue { MessageQueue requests; MessageQueue responses; }; // // public functions // template bool MessageQueue::tryPushMaybeMove(TMessage& message) { unsigned oldWritePos = writePos.load(std::memory_order_relaxed); unsigned newWritePos = 0; do { newWritePos = (oldWritePos + 1) % bufferSize; if (newWritePos == readPos) { return false; } } while (!writePos.compare_exchange_weak(oldWritePos, newWritePos, std::memory_order_release, std::memory_order_relaxed)); while (messageReady.get(oldWritePos)) { std::this_thread::yield(); // someone is still reading, wait... } messages[oldWritePos] = std::move(message); messageReady.set(oldWritePos, true); return true; } template void MessageQueue::push(TMessage message) { while (!tryPushMaybeMove(message)) { std::this_thread::yield(); } } template std::optional MessageQueue::tryPop() { unsigned oldReadPos = readPos.load(std::memory_order_relaxed); unsigned newReadPos = 0; do { if (oldReadPos == writePos) { return std::nullopt; } newReadPos = (oldReadPos + 1) % bufferSize; } while (!readPos.compare_exchange_weak(oldReadPos, newReadPos, std::memory_order_release, std::memory_order_relaxed)); while (!messageReady.get(oldReadPos)) { std::this_thread::yield(); // no harm in busy-waiting here, should be fast }; TMessage message = std::move(messages[oldReadPos]); messageReady.set(oldReadPos, false); return message; } template TMessage MessageQueue::wait() { while (true) { std::optional message = tryPop(); if (message.has_value()) { return message.value(); } std::this_thread::yield(); } } } // namespace mijin #endif // !defined(MIJIN_MESSAGE_QUEUE_HPP_INCLUDED)