111 using duration = std::chrono::steady_clock::duration;
124 std::shared_ptr<periodic_task_state> periodic_value = {})
131 if (
started.load(std::memory_order_acquire))
133 cancellation->pool_stopped.store(
true, std::memory_order_release);
135 periodic->running.store(
false, std::memory_order_release);
152 [[nodiscard]]
static auto
159 [[nodiscard]]
static auto
163 if (interval <= duration::zero())
164 throw std::invalid_argument(
"scheduled_pool periodic interval must be positive");
176 return cancellation_;
181 return periodic_task_;
186 return std::move(one_shot_task_);
197 auto const interval = *interval_;
200 throw std::system_error(advanced.error(),
"scheduled_pool periodic deadline");
201 next_run_ = advanced.value();
202 if (next_run_ <= now)
204 auto const quotient = (now - next_run_) / interval;
205 auto const maximum = (std::numeric_limits<
decltype(quotient)>::max)();
206 if (quotient == maximum)
207 throw std::system_error(std::make_error_code(std::errc::value_too_large),
208 "scheduled_pool periodic deadline");
209 auto const missed = quotient + 1;
210 if (missed > maximum / interval.count())
211 throw std::system_error(std::make_error_code(std::errc::value_too_large),
212 "scheduled_pool periodic deadline");
215 throw std::system_error(advanced.error(),
"scheduled_pool periodic deadline");
216 next_run_ = advanced.value();
222 std::shared_ptr<detail::scheduled_cancellation_state>
cancellation)
223 : id_(id), next_run_(run_time), one_shot_task_(
std::move(task)), cancellation_(
std::move(
cancellation)),
229 std::shared_ptr<detail::scheduled_cancellation_state>
cancellation)
230 : id_(id), next_run_(run_time), interval_(interval),
231 periodic_task_(
std::make_shared<periodic_task_state>(
std::move(task))),
238 std::optional<duration> interval_;
240 std::shared_ptr<periodic_task_state> periodic_task_;
241 std::shared_ptr<detail::scheduled_cancellation_state> cancellation_;
251 : pool_(worker_threads), stop_(false), next_task_id_(1)
256 template <
typename T = PoolType, std::enable_if_t<std::is_constructible_v<T,
size_t,
bool>,
int> = 0>
258 : pool_(worker_threads, register_workers), stop_(false), next_task_id_(1)
282 throw std::system_error(run_time.error(),
"scheduled_pool delay");
286 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, task_type>,
int> = 0>
292 throw std::system_error(run_time.error(),
"scheduled_pool delay");
308 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, task_type>,
int> = 0>
330 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, task_type>,
int> = 0>
347 if (interval <= duration::zero())
348 throw std::invalid_argument(
"scheduled_pool periodic interval must be positive");
351 throw std::system_error(run_time.error(),
"scheduled_pool initial delay");
354 throw std::system_error(next_run.error(),
"scheduled_pool periodic deadline");
358 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, task_type>,
int> = 0>
362 if (interval <= duration::zero())
363 throw std::invalid_argument(
"scheduled_pool periodic interval must be positive");
366 throw std::system_error(run_time.error(),
"scheduled_pool initial delay");
369 throw std::system_error(next_run.error(),
"scheduled_pool periodic deadline");
370 return insert_periodic_task(run_time.value(), interval,
392 std::lock_guard<std::mutex> lock(mutex_);
393 return scheduled_tasks_.size();
399 return pool_.is_current_worker() || current_scheduler ==
this;
419 std::lock_guard<std::recursive_mutex> shutdown_lock(shutdown_mutex_);
420 std::multimap<time_point, scheduled_task_info> discarded;
422 std::lock_guard<std::mutex> lock(mutex_);
426 for (
auto const& task : scheduled_tasks_)
427 task.second.cancellation()->pool_stopped.store(
true, std::memory_order_release);
428 scheduled_tasks_.swap(discarded);
433 condition_.notify_one();
436 std::lock_guard<std::mutex> scheduler_lock(scheduler_mutex_);
439 scheduler_thread_.
join();
444 pool_.shutdown(policy);
456 return pool_.configure_threads(name_prefix, policy, priority);
462 return pool_.configure_threads(config);
468 if (current_scheduler ==
this)
470 std::lock_guard<std::mutex> scheduler_lock(scheduler_mutex_);
482 if (!info.has_value())
483 return unexpected(std::make_error_code(std::errc::no_such_process));
491 if (!info.has_value())
492 return unexpected(std::make_error_code(std::errc::no_such_process));
493 return info->configure(config);
501 mutable std::mutex mutex_;
502 std::condition_variable condition_;
503 std::atomic<bool> stop_;
504 std::recursive_mutex shutdown_mutex_;
505 mutable std::mutex scheduler_mutex_;
507 std::multimap<time_point, scheduled_task_info> scheduled_tasks_;
508 std::atomic<uint64_t> next_task_id_;
510 inline static thread_local scheduled_pool_backend_base* current_scheduler =
nullptr;
515 std::promise<native_thread_id> scheduler_started;
516 auto scheduler_ready = scheduler_started.get_future();
519 [
this, started = std::move(scheduler_started)]()
mutable
521 current_scheduler =
this;
524 current_scheduler =
nullptr;
527 scheduler_tid_ = scheduler_ready.get();
528 (void)thread_info(scheduler_tid_).set_name(
"ts_sched_pool");
534 std::lock_guard<std::mutex> lock(mutex_);
536 uint64_t
const task_id = next_task_id_++;
537 scheduled_task_backend handle(task_id);
545 scheduled_tasks_.insert(
547 condition_.notify_one();
555 std::lock_guard<std::mutex> lock(mutex_);
557 uint64_t
const task_id = next_task_id_++;
558 scheduled_task_backend handle(task_id);
567 handle.get_cancellation()) });
568 condition_.notify_one();
578 std::unique_lock<std::mutex> lock(mutex_);
584 if (scheduled_tasks_.empty())
586 condition_.wait(lock, [
this] {
return stop_ || !scheduled_tasks_.empty(); });
593 auto const now = std::chrono::steady_clock::now();
594 auto it = scheduled_tasks_.begin();
596 if (it == scheduled_tasks_.end())
607 auto const next_run = it->first;
608 condition_.wait_until(lock, next_run);
617 scheduled_task_info info = std::move(it->second);
618 scheduled_tasks_.erase(it);
622 if (info.cancellation()->user_cancelled.load(std::memory_order_acquire)
623 || info.cancellation()->pool_stopped.load(std::memory_order_acquire))
631 auto cancellation = info.cancellation();
634 auto periodic_task = info.periodic_state();
635 bool const already_running = periodic_task->running.exchange(
true, std::memory_order_acq_rel);
636 if (!already_running)
638 auto dispatch = std::make_shared<scheduled_dispatch_state>(cancellation, periodic_task);
642 [periodic_task, cancellation, dispatch]()
644 dispatch->started.store(
true, std::memory_order_release);
647 if (!cancellation->user_cancelled.load(std::memory_order_acquire))
648 periodic_task->task();
652 periodic_task->running.store(
false, std::memory_order_release);
655 periodic_task->running.store(
false, std::memory_order_release);
660 periodic_task->running.store(
false, std::memory_order_release);
665 info.advance(std::chrono::steady_clock::now());
666 std::lock_guard<std::mutex> schedule_lock(mutex_);
667 if (!stop_ && !cancellation->user_cancelled.load(std::memory_order_acquire))
668 scheduled_tasks_.insert({ info.next_run(), std::move(info) });
672 auto one_shot_task = info.take_one_shot();
673 auto dispatch = std::make_shared<scheduled_dispatch_state>(cancellation);
675 [task = std::move(one_shot_task), cancellation, dispatch]()
mutable
677 dispatch->started.store(
true, std::memory_order_release);
678 if (!cancellation->user_cancelled.load(std::memory_order_acquire))
687 info.cancellation()->pool_stopped.store(
true, std::memory_order_release);