110 explicit State(
size_t max_queue_size)
111 : max_queue_size_(max_queue_size) {}
114 std::pair<bool, std::future<void>> submit(F&& task) {
115 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
116 if (!running_.load(std::memory_order_acquire)) {
117 rejected_.fetch_add(1, std::memory_order_relaxed);
118 return {
false, std::future<void>()};
120 if (!try_reserve_pending_slot()) {
121 rejected_.fetch_add(1, std::memory_order_relaxed);
122 return {
false, std::future<void>()};
125 auto result = detail::enqueue_reserved_task(
126 queue_, pending_, rejected_,
127 [
this, t = std::forward<F>(task)]()
mutable {
128 if (!claim_task_start()) {
129 throw std::runtime_error(
"RequestQueue is closed");
133 if (result.first) cv_.notify_one();
138 const bool caller_is_worker = current_worker_ ==
this;
139 const auto caller_id = std::this_thread::get_id();
141 std::unique_lock<std::mutex> lock(lifecycle_mutex_);
142 if (close_started_) {
143 if (caller_is_worker)
return;
144 cv_.wait(lock, [
this] {
return close_complete_; });
148 close_started_ =
true;
149 worker_initiated_close_ = caller_is_worker;
150 running_.store(
false, std::memory_order_release);
154 if (caller_is_worker) {
159 for (
auto& worker : workers_) {
160 if (!worker.joinable())
continue;
161 if (worker.get_id() == caller_id) {
167 cancel_pending_tasks();
169 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
170 cancellation_complete_ =
true;
175 for (
auto& worker : workers_) {
176 if (worker.joinable()) worker.join();
178 cancel_pending_tasks();
180 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
181 cancellation_complete_ =
true;
182 close_complete_ =
true;
187 bool is_closed() const noexcept {
188 return !running_.load(std::memory_order_acquire);
191 Stats stats()
const {
193 pending_.load(std::memory_order_relaxed),
194 active_.load(std::memory_order_relaxed),
195 completed_.load(std::memory_order_relaxed),
196 rejected_.load(std::memory_order_relaxed),
203 current_worker_ =
this;
206 current_worker_ =
nullptr;
209 std::mutex lifecycle_mutex_;
210 std::condition_variable cv_;
211 std::vector<std::thread> workers_;
212 std::atomic<bool> running_{
true};
213 std::atomic<size_t> pending_{0};
214 std::atomic<size_t> active_{0};
215 std::atomic<size_t> completed_{0};
216 std::atomic<size_t> rejected_{0};
217 size_t max_queue_size_;
218 size_t workers_alive_{0};
219 bool close_started_{
false};
220 bool close_complete_{
false};
221 bool cancellation_complete_{
false};
222 bool worker_initiated_close_{
false};
223 inline static thread_local State* current_worker_ =
nullptr;
226 bool try_reserve_pending_slot() {
227 size_t pending = pending_.load(std::memory_order_relaxed);
228 while (pending < max_queue_size_) {
229 if (pending_.compare_exchange_weak(
230 pending, pending + 1,
231 std::memory_order_acq_rel,
232 std::memory_order_relaxed)) {
239 bool claim_task_start() {
240 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
241 return running_.load(std::memory_order_acquire);
245 std::function<void()> task;
246 while (running_.load(std::memory_order_acquire)) {
247 if (queue_.try_dequeue(task)) {
248 pending_.fetch_sub(1, std::memory_order_acq_rel);
252 std::unique_lock<std::mutex> lock(lifecycle_mutex_);
253 cv_.wait_for(lock, std::chrono::milliseconds(50), [
this] {
254 return !running_.load(std::memory_order_acquire)
255 || queue_.size_approx() > 0;
261 void worker_exited() {
262 bool finalize_worker_close =
false;
264 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
266 finalize_worker_close = close_started_
267 && worker_initiated_close_
269 && workers_alive_ == 0;
271 if (!finalize_worker_close)
return;
273 bool cancel_pending =
false;
275 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
276 cancel_pending = !cancellation_complete_;
278 if (cancel_pending) cancel_pending_tasks();
280 std::lock_guard<std::mutex> lock(lifecycle_mutex_);
281 cancellation_complete_ =
true;
282 close_complete_ =
true;
287 void cancel_pending_tasks() {
288 std::function<void()> task;
289 while (queue_.try_dequeue(task)) {
290 pending_.fetch_sub(1, std::memory_order_acq_rel);
296 completed_.fetch_add(1, std::memory_order_relaxed);
300 void execute_task(std::function<
void()>& task) {
301 active_.fetch_add(1, std::memory_order_relaxed);
308 active_.fetch_sub(1, std::memory_order_relaxed);
309 completed_.fetch_add(1, std::memory_order_relaxed);
312 moodycamel::ConcurrentQueue<std::function<void()>> queue_;
323 : state_(std::make_shared<State>(max_queue_size))
325 if (num_workers == 0) {
326 throw std::invalid_argument(
"RequestQueue requires at least one worker");
329 for (
size_t i = 0; i < num_workers; ++i) {
368 std::pair<bool, std::future<void>>
submit(F&& task) {
369 return state_->submit(std::forward<F>(task));
388 return state_->is_closed();
396 return state_->stats();
400 void start_worker() {
402 std::lock_guard<std::mutex> lock(state_->lifecycle_mutex_);
403 ++state_->workers_alive_;
407 state_->workers_.emplace_back([state] { state->run_worker(); });
409 std::lock_guard<std::mutex> lock(state_->lifecycle_mutex_);
410 --state_->workers_alive_;
415 std::shared_ptr<State> state_;