[med-svn] [Git][med-team/libatomic-queue][master] 4 commits: debian: add gbp config file

Stephan Lachnit (@stephanlachnit) gitlab at salsa.debian.org
Fri Aug 22 10:42:52 BST 2025



Stephan Lachnit pushed to branch master at Debian Med / libatomic-queue


Commits:
2a14734c by Stephan Lachnit at 2025-08-22T11:37:48+02:00
debian: add gbp config file

- - - - -
ae3778ec by Stephan Lachnit at 2025-08-22T11:37:58+02:00
New upstream version 1.7.1
- - - - -
c9c88176 by Stephan Lachnit at 2025-08-22T11:38:02+02:00
Update upstream source from tag 'upstream/1.7.1'

Update to upstream version '1.7.1'
with Debian dir 6aaaa191e2b60f3516939724bab3f1664fefaa67
- - - - -
e697ea36 by Stephan Lachnit at 2025-08-22T11:42:19+02:00
Debian Release 1.7.1-1

- - - - -


9 changed files:

- .github/workflows/ci.yml
- − .gitignore
- Makefile
- README.md
- debian/changelog
- + debian/gbp.conf
- include/atomic_queue/atomic_queue.h
- include/atomic_queue/defs.h
- src/tests.cc


Changes:

=====================================
.github/workflows/ci.yml
=====================================
@@ -30,6 +30,5 @@ jobs:
     - name: Build and run unit tests
       run: make -rj2 TOOLSET=${{ matrix.toolset }} example run_tests
 
-    - if: ${{ matrix.sanitize }}
-      name: Build and run unit tests with thread sanitizer
+    - name: Build and run unit tests with thread sanitizer
       run: make -rj2 TOOLSET=${{ matrix.toolset }} BUILD=sanitize run_tests


=====================================
.gitignore deleted
=====================================
@@ -1,3 +0,0 @@
-*~
-build/
-__pycache__/


=====================================
Makefile
=====================================
@@ -165,7 +165,7 @@ run_benchmarks : ${build_dir}/benchmarks
 
 run_tests : ${build_dir}/tests
 	@echo "---- running $< ----"
-	$<
+	$< --log_level=warning
 
 run_% : ${build_dir}/%
 	@echo "---- running $< ----"


=====================================
README.md
=====================================
@@ -20,7 +20,9 @@ C++14 multiple-producer-multiple-consumer *lock-free* queues based on circular b
 
 Designed with a goal to minimize the latency between one thread pushing an element into a queue and another thread popping it from the queue.
 
-It has been developed, tested and benchmarked on Linux, but should support any C++14 platforms which implement `std::atomic`. Reported as compatible with Windows, but the continuous integrations hosted by GitHub are currently set up only for x86_64 platform on Ubuntu-22.04 and Ubuntu-24.04. Pull requests to extend the [continuous integrations][18] to run on other architectures and/or platforms are welcome.
+It has been developed, tested and benchmarked on Linux. Yet, any C++14 platform implementing `std::atomic` is expected to compile the unit-tests and run them without failures just as well.
+
+The unit-tests build and succeed on Windows, but the continuous integrations hosted by GitHub are currently set up only for x86_64 platform on Ubuntu-22.04 and Ubuntu-24.04. Pull requests to extend the [continuous integrations][18] to run on other architectures and/or platforms are welcome.
 
 ## Design Principles
 When minimizing latency a good design is not when there is nothing left to add, but rather when there is nothing left to remove, as these queues exemplify.


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+libatomic-queue (1.7.1-1) unstable; urgency=medium
+
+  * New upstream version 1.7.1
+
+ -- Stephan Lachnit <stephanlachnit at debian.org>  Fri, 22 Aug 2025 11:38:23 +0200
+
 libatomic-queue (1.6.9-1) experimental; urgency=medium
 
   * New upstream version 1.6.9


=====================================
debian/gbp.conf
=====================================
@@ -0,0 +1,3 @@
+[DEFAULT]
+pristine-tar = True
+filter = .gitignore


=====================================
include/atomic_queue/atomic_queue.h
=====================================
@@ -56,19 +56,19 @@ struct GetIndexShuffleBits<false, array_size, elements_per_cache_line> {
 // the element within the cache line) with the next N bits (which are the index of the cache line)
 // of the element index.
 template<int BITS>
-constexpr unsigned remap_index(unsigned index) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr unsigned remap_index(unsigned index) noexcept {
     unsigned constexpr mix_mask{(1u << BITS) - 1};
     unsigned const mix{(index ^ (index >> BITS)) & mix_mask};
     return index ^ mix ^ (mix << BITS);
 }
 
 template<>
-constexpr unsigned remap_index<0>(unsigned index) noexcept {
+ATOMIC_QUEUE_INLINE constexpr unsigned remap_index<0>(unsigned index) noexcept {
     return index;
 }
 
 template<int BITS, class T>
-constexpr T& map(T* elements, unsigned index) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr T& map(T* elements, unsigned index) noexcept {
     return elements[remap_index<BITS>(index)];
 }
 
@@ -87,30 +87,30 @@ constexpr T& map(T* elements, unsigned index) noexcept {
 // ++a;
 
 template<class T>
-constexpr T decrement(T x) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr T decrement(T x) noexcept {
     return x - 1;
 }
 
 template<class T>
-constexpr T increment(T x) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr T increment(T x) noexcept {
     return x + 1;
 }
 
 template<class T>
-constexpr T or_equal(T x, unsigned u) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr T or_equal(T x, unsigned u) noexcept {
     return x | x >> u;
 }
 
 template<class T, class... Args>
-constexpr T or_equal(T x, unsigned u, Args... rest) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr T or_equal(T x, unsigned u, Args... rest) noexcept {
     return or_equal(or_equal(x, u), rest...);
 }
 
-constexpr uint32_t round_up_to_power_of_2(uint32_t a) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr uint32_t round_up_to_power_of_2(uint32_t a) noexcept {
     return increment(or_equal(decrement(a), 1, 2, 4, 8, 16));
 }
 
-constexpr uint64_t round_up_to_power_of_2(uint64_t a) noexcept {
+ATOMIC_QUEUE_INLINE static constexpr uint64_t round_up_to_power_of_2(uint64_t a) noexcept {
     return increment(or_equal(decrement(a), 1, 2, 4, 8, 16, 32));
 }
 
@@ -125,11 +125,23 @@ constexpr T nil() noexcept {
 }
 
 template<class T>
-inline void destroy_n(T* p, unsigned n) noexcept {
+ATOMIC_QUEUE_INLINE static void destroy_n(T* p, unsigned n) noexcept {
     for(auto q = p + n; p != q;)
         (p++)->~T();
 }
 
+template<class T>
+ATOMIC_QUEUE_INLINE static void swap_relaxed(std::atomic<T>& a, std::atomic<T>& b) noexcept {
+    auto a2 = a.load(X);
+    a.store(b.load(X), X);
+    b.store(a2, X);
+}
+
+template<class T>
+ATOMIC_QUEUE_INLINE static void copy_relaxed(std::atomic<T>& a, std::atomic<T> const& b) noexcept {
+    a.store(b.load(X), X);
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 } // namespace details
@@ -152,22 +164,19 @@ protected:
         , tail_(b.tail_.load(X)) {}
 
     AtomicQueueCommon& operator=(AtomicQueueCommon const& b) noexcept {
-        head_.store(b.head_.load(X), X);
-        tail_.store(b.tail_.load(X), X);
+        details::copy_relaxed(head_, b.head_);
+        details::copy_relaxed(tail_, b.tail_);
         return *this;
     }
 
+    // Relatively semi-special swap is not thread-safe either.
     void swap(AtomicQueueCommon& b) noexcept {
-        unsigned h = head_.load(X);
-        unsigned t = tail_.load(X);
-        head_.store(b.head_.load(X), X);
-        tail_.store(b.tail_.load(X), X);
-        b.head_.store(h, X);
-        b.tail_.store(t, X);
+        details::swap_relaxed(head_, b.head_);
+        details::swap_relaxed(tail_, b.tail_);
     }
 
     template<class T, T NIL>
-    static T do_pop_atomic(std::atomic<T>& q_element) noexcept {
+    ATOMIC_QUEUE_INLINE static T do_pop_atomic(std::atomic<T>& q_element) noexcept {
         if(Derived::spsc_) {
             for(;;) {
                 T element = q_element.load(A);
@@ -193,7 +202,7 @@ protected:
     }
 
     template<class T, T NIL>
-    static void do_push_atomic(T element, std::atomic<T>& q_element) noexcept {
+    ATOMIC_QUEUE_INLINE static void do_push_atomic(T element, std::atomic<T>& q_element) noexcept {
         assert(element != NIL);
         if(Derived::spsc_) {
             while(ATOMIC_QUEUE_UNLIKELY(q_element.load(X) != NIL))
@@ -213,7 +222,7 @@ protected:
     enum State : unsigned char { EMPTY, STORING, STORED, LOADING };
 
     template<class T>
-    static T do_pop_any(std::atomic<unsigned char>& state, T& q_element) noexcept {
+    ATOMIC_QUEUE_INLINE static T do_pop_any(std::atomic<unsigned char>& state, T& q_element) noexcept {
         if(Derived::spsc_) {
             while(ATOMIC_QUEUE_UNLIKELY(state.load(A) != STORED))
                 if(Derived::maximize_throughput_)
@@ -239,7 +248,7 @@ protected:
     }
 
     template<class U, class T>
-    static void do_push_any(U&& element, std::atomic<unsigned char>& state, T& q_element) noexcept {
+    ATOMIC_QUEUE_INLINE static void do_push_any(U&& element, std::atomic<unsigned char>& state, T& q_element) noexcept {
         if(Derived::spsc_) {
             while(ATOMIC_QUEUE_UNLIKELY(state.load(A) != EMPTY))
                 if(Derived::maximize_throughput_)
@@ -265,7 +274,7 @@ protected:
 
 public:
     template<class T>
-    bool try_push(T&& element) noexcept {
+    ATOMIC_QUEUE_INLINE bool try_push(T&& element) noexcept {
         auto head = head_.load(X);
         if(Derived::spsc_) {
             if(static_cast<int>(head - tail_.load(X)) >= static_cast<int>(static_cast<Derived&>(*this).size_))
@@ -284,7 +293,7 @@ public:
     }
 
     template<class T>
-    bool try_pop(T& element) noexcept {
+    ATOMIC_QUEUE_INLINE bool try_pop(T& element) noexcept {
         auto tail = tail_.load(X);
         if(Derived::spsc_) {
             if(static_cast<int>(head_.load(X) - tail) <= 0)
@@ -303,7 +312,7 @@ public:
     }
 
     template<class T>
-    void push(T&& element) noexcept {
+    ATOMIC_QUEUE_INLINE void push(T&& element) noexcept {
         unsigned head;
         if(Derived::spsc_) {
             head = head_.load(X);
@@ -316,7 +325,7 @@ public:
         static_cast<Derived&>(*this).do_push(std::forward<T>(element), head);
     }
 
-    auto pop() noexcept {
+    ATOMIC_QUEUE_INLINE auto pop() noexcept {
         unsigned tail;
         if(Derived::spsc_) {
             tail = tail_.load(X);
@@ -329,20 +338,21 @@ public:
         return static_cast<Derived&>(*this).do_pop(tail);
     }
 
-    bool was_empty() const noexcept {
+    ATOMIC_QUEUE_INLINE bool was_empty() const noexcept {
         return !was_size();
     }
 
-    bool was_full() const noexcept {
+    ATOMIC_QUEUE_INLINE bool was_full() const noexcept {
         return was_size() >= capacity();
     }
 
-    unsigned was_size() const noexcept {
+    ATOMIC_QUEUE_INLINE unsigned was_size() const noexcept {
         // tail_ can be greater than head_ because of consumers doing pop, rather that try_pop, when the queue is empty.
-        return std::max(static_cast<int>(head_.load(X) - tail_.load(X)), 0);
+        unsigned n{head_.load(X) - tail_.load(X)};
+        return static_cast<int>(n) < 0 ? 0 : n; // Windows headers break std::min/max by default. Do std::max<int>(n, 0) the hard way here.
     }
 
-    unsigned capacity() const noexcept {
+    ATOMIC_QUEUE_INLINE unsigned capacity() const noexcept {
         return static_cast<Derived const&>(*this).size_;
     }
 };
@@ -362,12 +372,12 @@ class AtomicQueue : public AtomicQueueCommon<AtomicQueue<T, SIZE, NIL, MINIMIZE_
 
     alignas(CACHE_LINE_SIZE) std::atomic<T> elements_[size_];
 
-    T do_pop(unsigned tail) noexcept {
+    ATOMIC_QUEUE_INLINE T do_pop(unsigned tail) noexcept {
         std::atomic<T>& q_element = details::map<SHUFFLE_BITS>(elements_, tail % size_);
         return Base::template do_pop_atomic<T, NIL>(q_element);
     }
 
-    void do_push(T element, unsigned head) noexcept {
+    ATOMIC_QUEUE_INLINE void do_push(T element, unsigned head) noexcept {
         std::atomic<T>& q_element = details::map<SHUFFLE_BITS>(elements_, head % size_);
         Base::template do_push_atomic<T, NIL>(element, q_element);
     }
@@ -402,13 +412,13 @@ class AtomicQueue2 : public AtomicQueueCommon<AtomicQueue2<T, SIZE, MINIMIZE_CON
     alignas(CACHE_LINE_SIZE) std::atomic<unsigned char> states_[size_] = {};
     alignas(CACHE_LINE_SIZE) T elements_[size_] = {};
 
-    T do_pop(unsigned tail) noexcept {
+    ATOMIC_QUEUE_INLINE T do_pop(unsigned tail) noexcept {
         unsigned index = details::remap_index<SHUFFLE_BITS>(tail % size_);
         return Base::do_pop_any(states_[index], elements_[index]);
     }
 
     template<class U>
-    void do_push(U&& element, unsigned head) noexcept {
+    ATOMIC_QUEUE_INLINE void do_push(U&& element, unsigned head) noexcept {
         unsigned index = details::remap_index<SHUFFLE_BITS>(head % size_);
         Base::do_push_any(std::forward<U>(element), states_[index], elements_[index]);
     }
@@ -445,12 +455,12 @@ class AtomicQueueB : private std::allocator_traits<A>::template rebind_alloc<std
     alignas(CACHE_LINE_SIZE) unsigned size_;
     std::atomic<T>* elements_;
 
-    T do_pop(unsigned tail) noexcept {
+    ATOMIC_QUEUE_INLINE T do_pop(unsigned tail) noexcept {
         std::atomic<T>& q_element = details::map<SHUFFLE_BITS>(elements_, tail & (size_ - 1));
         return Base::template do_pop_atomic<T, NIL>(q_element);
     }
 
-    void do_push(T element, unsigned head) noexcept {
+    ATOMIC_QUEUE_INLINE void do_push(T element, unsigned head) noexcept {
         std::atomic<T>& q_element = details::map<SHUFFLE_BITS>(elements_, head & (size_ - 1));
         Base::template do_push_atomic<T, NIL>(element, q_element);
     }
@@ -533,13 +543,13 @@ class AtomicQueueB2 : private std::allocator_traits<A>::template rebind_alloc<un
     static constexpr auto SHUFFLE_BITS = details::GetCacheLineIndexBits<STATES_PER_CACHE_LINE>::value;
     static_assert(SHUFFLE_BITS, "Unexpected SHUFFLE_BITS.");
 
-    T do_pop(unsigned tail) noexcept {
+    ATOMIC_QUEUE_INLINE T do_pop(unsigned tail) noexcept {
         unsigned index = details::remap_index<SHUFFLE_BITS>(tail & (size_ - 1));
         return Base::do_pop_any(states_[index], elements_[index]);
     }
 
     template<class U>
-    void do_push(U&& element, unsigned head) noexcept {
+    ATOMIC_QUEUE_INLINE void do_push(U&& element, unsigned head) noexcept {
         unsigned index = details::remap_index<SHUFFLE_BITS>(head & (size_ - 1));
         Base::do_push_any(std::forward<U>(element), states_[index], elements_[index]);
     }
@@ -624,12 +634,12 @@ struct RetryDecorator : Queue {
 
     using Queue::Queue;
 
-    void push(T element) noexcept {
+    ATOMIC_QUEUE_INLINE void push(T element) noexcept {
         while(!this->try_push(element))
             spin_loop_pause();
     }
 
-    T pop() noexcept {
+    ATOMIC_QUEUE_INLINE T pop() noexcept {
         T element;
         while(!this->try_pop(element))
             spin_loop_pause();


=====================================
include/atomic_queue/defs.h
=====================================
@@ -84,10 +84,12 @@ namespace atomic_queue {
 #define ATOMIC_QUEUE_LIKELY(expr) __builtin_expect(static_cast<bool>(expr), 1)
 #define ATOMIC_QUEUE_UNLIKELY(expr) __builtin_expect(static_cast<bool>(expr), 0)
 #define ATOMIC_QUEUE_NOINLINE __attribute__((noinline))
+#define ATOMIC_QUEUE_INLINE inline __attribute__((always_inline))
 #else
 #define ATOMIC_QUEUE_LIKELY(expr) (expr)
 #define ATOMIC_QUEUE_UNLIKELY(expr) (expr)
 #define ATOMIC_QUEUE_NOINLINE
+#define ATOMIC_QUEUE_INLINE inline
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


=====================================
src/tests.cc
=====================================
@@ -26,6 +26,8 @@ void stress() {
     constexpr int PRODUCERS = 3;
     constexpr int CONSUMERS = 3;
     constexpr unsigned N = 1000000;
+    using T = typename Queue::value_type;
+    constexpr T STOP = -1;
 
     Queue q;
     Barrier barrier;
@@ -34,7 +36,7 @@ void stress() {
     for(unsigned i = 0; i < PRODUCERS; ++i)
         producers[i] = std::thread([&q, &barrier, N=N]() {
             barrier.wait();
-            for(unsigned n = N; n; --n)
+            for(T n = N; n; --n)
                 q.push(n);
         });
 
@@ -44,31 +46,27 @@ void stress() {
         consumers[i] = std::thread([&q, &barrier, &r = results[i]]() {
             barrier.wait();
             uint64_t result = 0;
-            for(;;) {
-                unsigned n = q.pop();
+            for(T n; (n = q.pop()) != STOP;)
                 result += n;
-                if(n == 1)
-                    break;
-            }
             r = result;
         });
 
     barrier.release(PRODUCERS + CONSUMERS);
-
     for(auto& t : producers)
         t.join();
+    for(int i = CONSUMERS; i--;)
+        q.push(STOP);
     for(auto& t : consumers)
         t.join();
 
-    constexpr uint64_t expected_result = (N + 1) / 2. * N;
-
+    constexpr uint64_t expected_result = (N + 1) / 2. * N * PRODUCERS;
     uint64_t result = 0;
     for(auto& r : results) {
-        BOOST_WARN_GT(r, (expected_result / CONSUMERS) / 10); // Make sure a consumer didn't starve. False positives are possible here.
+        BOOST_WARN_GT(r, (expected_result / CONSUMERS / 10)); // Make sure a consumer didn't starve. False positives are possible here.
         result += r;
     }
 
-    int64_t result_diff = result / CONSUMERS - expected_result;
+    int64_t result_diff = result - expected_result;
     BOOST_CHECK_EQUAL(result_diff, 0);
 }
 



View it on GitLab: https://salsa.debian.org/med-team/libatomic-queue/-/compare/ad2de3d8a3d151513720a8337006834b46b65298...e697ea36f17f2c4d994715b70d1f4a736cad2027

-- 
View it on GitLab: https://salsa.debian.org/med-team/libatomic-queue/-/compare/ad2de3d8a3d151513720a8337006834b46b65298...e697ea36f17f2c4d994715b70d1f4a736cad2027
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20250822/abd18696/attachment-0001.htm>


More information about the debian-med-commit mailing list