MySQL 26.7.0
Source Code Documentation
sync_bounded_queue.h
Go to the documentation of this file.
1// Copyright (c) 2026, Oracle and/or its affiliates.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of MySQL hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License, version 2.0, for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
24#ifndef MYSQL_CONCURRENCY_SYNC_BOUNDED_QUEUE
25#define MYSQL_CONCURRENCY_SYNC_BOUNDED_QUEUE
26
27#include <array>
28#include <bit>
29#include <condition_variable>
30#include <cstdint>
31#include <limits>
32#include <ostream>
33
37
40
41namespace mysql::concurrency {
42
43template <uint64_t N>
44concept Is_power_of_two = std::has_single_bit(N);
45
46/// @brief Bounded concurrent queue supporting multiple producers and consumers.
47/// Designed to minimize contention using lock-free index allocation
48/// followed by per-slot locking for safe element access and
49/// notification.
50///
51/// ---Implementation:
52/// The queue uses a fixed-size circular buffer implemented as a std::array
53/// of Padded_slot structures. Each slot contains:
54/// - The element (T)
55/// - A bool validity flag indicating if the slot holds a valid element
56/// - A mutex (validity_mt) protecting the validity and element
57/// - Two condition variables: producer_cv (wait for empty slot) and
58/// consumer_cv (wait for valid element)
59///
60/// Producers:
61/// - Lock-free: Atomically fetch_add(1) on m_head to get a unique index
62/// (m_head is the next producer position).
63/// - Lock slot mutex, wait on producer_cv until !validity (empty slot).
64/// - Assign element to slot, set validity = true.
65/// - Unlock and notify one waiting consumer on consumer_cv.
66///
67/// Consumers:
68/// - Lock-free: Atomically fetch_add(1) on m_tail to get a unique index
69/// (m_tail is the next consumer position).
70/// - Lock slot mutex, wait on consumer_cv until validity (valid element).
71/// - If stop_predicate() is true, return empty pair with false.
72/// - Otherwise, move element, set validity = false.
73/// - Unlock and notify one waiting producer on producer_cv.
74///
75/// Indices wrap around using % m_capacity (capacity is power of 2).
76/// The queue has fixed capacity; enqueue blocks if full (no resizing).
77/// Contention is low if capacity >= num_producers + num_consumers.
78/// Index allocation is lock-free, but slot access uses per-slot mutex/CV.
79///
80/// @tparam T Type of element stored in the queue.
81/// @tparam capacity_tp Fixed capacity of the queue (must be power of 2).
82template <typename T, uint64_t capacity_tp>
85 static_assert(std::has_single_bit(capacity_tp),
86 "Capacity must be a power of 2");
87
88 public:
89 using Element_type = T;
92 using Mutex_type = std::mutex;
93 using Cv_type = std::condition_variable;
94 static constexpr uint64_t m_capacity = capacity_tp;
95
96 /// @brief Construct the queue. The memory_resource parameter is reserved
97 /// for future use and currently ignored.
99
100 /// @brief Enqueue (push) an element into the queue. Blocks the calling
101 /// producer if the queue is full (no space available).
102 /// @details
103 /// - Lock-free: Advances m_head via atomic fetch_add to get unique index.
104 /// - Locks the slot mutex and waits on producer_cv until the slot is empty
105 /// (!validity).
106 /// - Assigns the element and sets validity = true.
107 /// - Notifies one waiting consumer on consumer_cv.
108 /// The operation is lock-free for index allocation but uses per-slot locking
109 /// for safe access. Blocks only if full; choose capacity >= num_producers +
110 /// num_consumers to minimize blocking.
111 /// @param element Element to enqueue
112 /// @param stop_predicate Predicate checked after wakeup; if true and
113 /// still cannot enqueue, stops
114 /// waiting and returns {default T, false}.
115 template <typename P>
116 bool enqueue(Element_type &&element, P &&stop_predicate);
117
118 /// Overload without stop_predicate, using default that always returns false.
119 /// @param element Element to enqueue
120 bool enqueue(Element_type &&element);
121
122 /// @brief Dequeue an element from the queue. Called by consumers.
123 /// @param stop_predicate Predicate checked after wakeup; if true and
124 /// still cannot dequeue, stops
125 /// waiting and returns {default T, false}.
126 /// @return Pair of {consumed element, true} if successful, or {default T,
127 /// false}
128 /// if stopped by predicate.
129 /// @details
130 /// - Lock-free: Advances m_tail via atomic fetch_add to get unique index.
131 /// - Locks the slot mutex and waits on consumer_cv until the slot is valid
132 /// (validity == true).
133 /// - If stop_predicate() is true, returns without consuming.
134 /// - Otherwise, moves the element and sets validity = false.
135 /// - Notifies one waiting producer on producer_cv.
136 /// The operation is lock-free for index allocation but uses per-slot locking
137 /// for safe access. Blocks if empty until an element is available or stopped.
138 template <typename P>
139 [[nodiscard]] std::pair<Element_type, bool> dequeue(P &&stop_predicate);
140
141 /// @brief Check if the queue is empty.
142 /// @returns True when queue is empty, false otherwise.
143 [[nodiscard]] bool empty();
144
145 /// @brief Signal shutdown: Notifies all blocked consumers. Allows waiting
146 /// consumers to wake and check stop_predicate to exit gracefully. Producer
147 /// stop must be handled by the structure user, before calling "notify_all".
149
150 /// Destructor
151 /// Before deleting the queue, the caller must ensure that no thread
152 /// is or will be using the queue, for instance, by first calling notify_all
153 /// and then joining all consumer threads.
154 virtual ~Sync_bounded_queue() = default;
155
156 /// @brief Estimates the queue size based on m_head and m_tail positions.
157 /// @details Due to concurrent modifications, this is an approximation.
158 /// The actual size may be bigger than the returned value; the difference is
159 /// at most min(N,M) where N and M are the number of concurrent
160 /// enqueue/dequeue operations.
161 /// @return Estimated number of elements (unconsumed valid slots).
162 [[nodiscard]] std::size_t size() const;
163
164 /// @brief Reset the queue: Clears all slots (sets validity=false, elements
165 /// to default), resets m_head and m_tail to 0.
166 void reset();
167
168 private:
169 /// @brief Atomically increments the given position (m_head for producers,
170 /// m_tail for consumers) and returns the new index modulo capacity.
171 /// Used internally for lock-free index allocation.
172 /// @param[in,out] current Atomic position to advance (m_head or m_tail).
173 /// @return The granted index after increment (wrapped).
174 [[nodiscard]] std::size_t get_next_index(std::atomic<std::size_t> &current);
175
176 /// Padded slots containing elements and synchronization primitives
177 std::array<detail::Padded_slot<Element_type, Mutex_type, Cv_type>,
178 capacity_tp>
180 /// Current head idx, when at the end, wraps to 0
181 std::atomic<std::size_t> m_head{0};
182 /// Current tail idx, when at the end, wraps to 0
183 std::atomic<std::size_t> m_tail{0};
184};
185
186} // namespace mysql::concurrency
187
189
190#endif // MYSQL_CONCURRENCY_SYNC_BOUNDED_QUEUE
Allocator using a Memory_resource to do the allocation.
Definition: allocator.h:52
Polymorphism-free memory resource class with custom allocator and deallocator functions.
Definition: memory_resource.h:88
Bounded concurrent queue supporting multiple producers and consumers.
Definition: sync_bounded_queue.h:84
std::atomic< std::size_t > m_tail
Current tail idx, when at the end, wraps to 0.
Definition: sync_bounded_queue.h:183
void notify_all()
Signal shutdown: Notifies all blocked consumers.
std::size_t get_next_index(std::atomic< std::size_t > &current)
Atomically increments the given position (m_head for producers, m_tail for consumers) and returns the...
bool enqueue(Element_type &&element, P &&stop_predicate)
Enqueue (push) an element into the queue.
static constexpr uint64_t m_capacity
Definition: sync_bounded_queue.h:94
Sync_bounded_queue(Memory_resource={})
Construct the queue.
void reset()
Reset the queue: Clears all slots (sets validity=false, elements to default), resets m_head and m_tai...
virtual ~Sync_bounded_queue()=default
Destructor Before deleting the queue, the caller must ensure that no thread is or will be using the q...
std::pair< Element_type, bool > dequeue(P &&stop_predicate)
Dequeue an element from the queue.
bool enqueue(Element_type &&element)
Overload without stop_predicate, using default that always returns false.
std::size_t size() const
Estimates the queue size based on m_head and m_tail positions.
std::condition_variable Cv_type
Definition: sync_bounded_queue.h:93
bool empty()
Check if the queue is empty.
std::atomic< std::size_t > m_head
Current head idx, when at the end, wraps to 0.
Definition: sync_bounded_queue.h:181
std::mutex Mutex_type
Definition: sync_bounded_queue.h:92
std::array< detail::Padded_slot< Element_type, Mutex_type, Cv_type >, capacity_tp > m_slots
Padded slots containing elements and synchronization primitives.
Definition: sync_bounded_queue.h:179
T Element_type
Definition: sync_bounded_queue.h:89
Definition: sync_bounded_queue.h:44
#define P
Definition: dtoa.cc:620
#define T
Definition: jit_executor_value.cc:373
Allocator class that uses a polymorphic Memory_resource to allocate memory.
std::atomic< Type > N
Definition: ut0counter.h:225
Definition: cache_line_size.h:31