From c396b3f225b6c63bf27a8dbfb87bbc1bbfeb2771 Mon Sep 17 00:00:00 2001 From: offtkp Date: Thu, 8 Aug 2024 14:43:28 +0300 Subject: [PATCH] Add lockfree queue --- third_party/lockfree/LICENSE | 21 ++++ third_party/lockfree/lockfree/spsc/queue.hpp | 110 +++++++++++++++++ .../lockfree/lockfree/spsc/queue_impl.hpp | 111 ++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 third_party/lockfree/LICENSE create mode 100755 third_party/lockfree/lockfree/spsc/queue.hpp create mode 100644 third_party/lockfree/lockfree/spsc/queue_impl.hpp diff --git a/third_party/lockfree/LICENSE b/third_party/lockfree/LICENSE new file mode 100644 index 00000000..2cb6782f --- /dev/null +++ b/third_party/lockfree/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Djordje Nedic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/lockfree/lockfree/spsc/queue.hpp b/third_party/lockfree/lockfree/spsc/queue.hpp new file mode 100755 index 00000000..97a8dc3f --- /dev/null +++ b/third_party/lockfree/lockfree/spsc/queue.hpp @@ -0,0 +1,110 @@ +/************************************************************** + * @file queue.hpp + * @brief A queue implementation written in standard c++11 + * suitable for both low-end microcontrollers all the way + * to HPC machines. Lock-free for single consumer single + * producer scenarios. + **************************************************************/ + +/************************************************************** + * Copyright (c) 2023 Djordje Nedic + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * This file is part of lockfree + * + * Author: Djordje Nedic + * Version: v2.0.9 + **************************************************************/ + +/************************** INCLUDE ***************************/ +#ifndef LOCKFREE_QUEUE_HPP +#define LOCKFREE_QUEUE_HPP + +#include +#include +#include + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#include +#endif + +namespace lockfree { +namespace spsc { +/*************************** TYPES ****************************/ + +template class Queue { + static_assert(std::is_trivial::value, "The type T must be trivial"); + static_assert(size > 2, "Buffer size must be bigger than 2"); + + /********************** PUBLIC METHODS ************************/ + public: + Queue(); + + /** + * @brief Adds an element into the queue. + * Should only be called from the producer thread. + * @param[in] element + * @retval Operation success + */ + bool Push(const T &element); + + /** + * @brief Removes an element from the queue. + * Should only be called from the consumer thread. + * @param[out] element + * @retval Operation success + */ + bool Pop(T &element); + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + /** + * @brief Removes an element from the queue. + * Should only be called from the consumer thread. + * @retval Either the element or nothing + */ + std::optional PopOptional(); +#endif + + /********************** PRIVATE MEMBERS ***********************/ + private: + T _data[size]; /**< Data array */ +#if LOCKFREE_CACHE_COHERENT + alignas(LOCKFREE_CACHELINE_LENGTH) std::atomic_size_t _r; /**< Read index */ + alignas( + LOCKFREE_CACHELINE_LENGTH) std::atomic_size_t _w; /**< Write index */ +#else + std::atomic_size_t _r; /**< Read index */ + std::atomic_size_t _w; /**< Write index */ +#endif +}; + +} /* namespace spsc */ +} /* namespace lockfree */ + +/************************** INCLUDE ***************************/ + +/* Include the implementation */ +#include "queue_impl.hpp" + +#endif /* LOCKFREE_QUEUE_HPP */ diff --git a/third_party/lockfree/lockfree/spsc/queue_impl.hpp b/third_party/lockfree/lockfree/spsc/queue_impl.hpp new file mode 100644 index 00000000..43654c88 --- /dev/null +++ b/third_party/lockfree/lockfree/spsc/queue_impl.hpp @@ -0,0 +1,111 @@ +/************************************************************** + * @file queue_impl.hpp + * @brief A queue implementation written in standard c++11 + * suitable for both low-end microcontrollers all the way + * to HPC machines. Lock-free for single consumer single + * producer scenarios. + **************************************************************/ + +/************************************************************** + * Copyright (c) 2023 Djordje Nedic + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * This file is part of lockfree + * + * Author: Djordje Nedic + * Version: v2.0.9 + **************************************************************/ + +namespace lockfree { +namespace spsc { +/********************** PUBLIC METHODS ************************/ + +template Queue::Queue() : _r(0U), _w(0U) {} + +template bool Queue::Push(const T &element) { + /* + The full check needs to be performed using the next write index not to + miss the case when the read index wrapped and write index is at the end + */ + const size_t w = _w.load(std::memory_order_relaxed); + size_t w_next = w + 1; + if (w_next == size) { + w_next = 0U; + } + + /* Full check */ + const size_t r = _r.load(std::memory_order_acquire); + if (w_next == r) { + return false; + } + + /* Place the element */ + _data[w] = element; + + /* Store the next write index */ + _w.store(w_next, std::memory_order_release); + return true; +} + +template bool Queue::Pop(T &element) { + /* Preload indexes with adequate memory ordering */ + size_t r = _r.load(std::memory_order_relaxed); + const size_t w = _w.load(std::memory_order_acquire); + + /* Empty check */ + if (r == w) { + return false; + } + + /* Remove the element */ + element = _data[r]; + + /* Increment the read index */ + r++; + if (r == size) { + r = 0U; + } + + /* Store the read index */ + _r.store(r, std::memory_order_release); + return true; +} + +/********************* std::optional API **********************/ +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +template +std::optional Queue::PopOptional() { + T element; + bool result = Pop(element); + + if (result) { + return element; + } else { + return {}; + } +} +#endif + +} /* namespace spsc */ +} /* namespace lockfree */