From 853f6f371d5eae5692c9bae615aba8964a4781ed Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 20 Sep 2021 14:07:55 +0200 Subject: Added mpscqueue (for future use) --- zencore/include/zencore/mpscqueue.h | 109 ++++++++++++++++++++++++++++++++++++ zencore/include/zencore/zencore.h | 4 ++ zencore/mpscqueue.cpp | 22 ++++++++ zencore/zencore.cpp | 2 + zencore/zencore.vcxproj | 2 + zencore/zencore.vcxproj.filters | 2 + 6 files changed, 141 insertions(+) create mode 100644 zencore/include/zencore/mpscqueue.h create mode 100644 zencore/mpscqueue.cpp diff --git a/zencore/include/zencore/mpscqueue.h b/zencore/include/zencore/mpscqueue.h new file mode 100644 index 000000000..bb558bb5a --- /dev/null +++ b/zencore/include/zencore/mpscqueue.h @@ -0,0 +1,109 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include +#include +#include + +#ifdef __cpp_lib_hardware_interference_size +using std::hardware_constructive_interference_size; +using std::hardware_destructive_interference_size; +#else +// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ... +constexpr std::size_t hardware_constructive_interference_size = 64; +constexpr std::size_t hardware_destructive_interference_size = 64; +#endif + +namespace zen { + +/** An untyped array of data with compile-time alignment and size derived from another type. */ +template +struct TypeCompatibleStorage +{ + ElementType* Data() { return (ElementType*)this; } + const ElementType* Data() const { return (const ElementType*)this; } + + char alignas(ElementType) DataMember; +}; + +/** Fast multi-producer/single-consumer unbounded concurrent queue. + + Based on http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue + */ + +template +class MpscQueue final +{ +public: + using ElementType = T; + + MpscQueue() + { + Node* Sentinel = new Node; + Head.store(Sentinel, std::memory_order_relaxed); + Tail = Sentinel; + } + + ~MpscQueue() + { + Node* Next = Tail->Next.load(std::memory_order_relaxed); + + // sentinel's value is already destroyed + delete Tail; + + while (Next != nullptr) + { + Tail = Next; + Next = Tail->Next.load(std::memory_order_relaxed); + + std::destroy_at((ElementType*)&Tail->Value); + delete Tail; + } + } + + template + void Enqueue(ArgTypes&&... Args) + { + Node* New = new Node; + new (&New->Value) ElementType(std::forward(Args)...); + + Node* Prev = Head.exchange(New, std::memory_order_acq_rel); + Prev->Next.store(New, std::memory_order_release); + } + + std::optional Dequeue() + { + Node* Next = Tail->Next.load(std::memory_order_acquire); + + if (Next == nullptr) + { + return {}; + } + + ElementType* ValuePtr = (ElementType*)&Next->Value; + std::optional Res{std::move(*ValuePtr)}; + std::destroy_at(ValuePtr); + + delete Tail; // current sentinel + + Tail = Next; // new sentinel + return Res; + } + +private: + struct Node + { + std::atomic Next{nullptr}; + TypeCompatibleStorage Value; + }; + +private: + std::atomic Head; // accessed only by producers + alignas(hardware_constructive_interference_size) + Node* Tail; // accessed only by consumer, hence should be on a different cache line than `Head` +}; + +void mpscqueue_forcelink(); + +} // namespace zen diff --git a/zencore/include/zencore/zencore.h b/zencore/include/zencore/zencore.h index 4fad0b7a4..2b411af0e 100644 --- a/zencore/include/zencore/zencore.h +++ b/zencore/include/zencore/zencore.h @@ -60,6 +60,10 @@ # endif #endif +#if ZEN_COMPILER_MSC +# pragma warning(disable : 4324) // warning C4324: '': structure was padded due to alignment specifier +#endif + ////////////////////////////////////////////////////////////////////////// // Architecture // diff --git a/zencore/mpscqueue.cpp b/zencore/mpscqueue.cpp new file mode 100644 index 000000000..8b89ac31a --- /dev/null +++ b/zencore/mpscqueue.cpp @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include + +#include + +namespace zen { + +TEST_CASE("mpsc") +{ + MpscQueue Queue; + Queue.Enqueue("hello"); + std::optional Value = Queue.Dequeue(); + CHECK_EQ(Value, "hello"); +} + +void +mpscqueue_forcelink() +{ +} + +} // namespace zen \ No newline at end of file diff --git a/zencore/zencore.cpp b/zencore/zencore.cpp index b025f5b5c..1ea8ceb37 100644 --- a/zencore/zencore.cpp +++ b/zencore/zencore.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ zencore_forcelinktests() zen::intmath_forcelink(); zen::iobuffer_forcelink(); zen::memory_forcelink(); + zen::mpscqueue_forcelink(); zen::refcount_forcelink(); zen::sha1_forcelink(); zen::stats_forcelink(); diff --git a/zencore/zencore.vcxproj b/zencore/zencore.vcxproj index f0eae411e..2322f7173 100644 --- a/zencore/zencore.vcxproj +++ b/zencore/zencore.vcxproj @@ -128,6 +128,7 @@ + @@ -167,6 +168,7 @@ + diff --git a/zencore/zencore.vcxproj.filters b/zencore/zencore.vcxproj.filters index b9c69a33f..d2e7a3159 100644 --- a/zencore/zencore.vcxproj.filters +++ b/zencore/zencore.vcxproj.filters @@ -43,6 +43,7 @@ + @@ -75,6 +76,7 @@ + -- cgit v1.2.3