[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