12#include <condition_variable>
44 cancelled_->store(
true, std::memory_order_release);
49 return cancelled_->load(std::memory_order_acquire);
52 [[nodiscard]]
auto id() const noexcept -> uint64_t
59 std::shared_ptr<std::atomic<bool>> cancelled_;
63 [[nodiscard]]
auto get_cancel_flag() const -> std::shared_ptr<std::atomic<
bool>>
138template <
typename PoolType = ThreadPool>
142 using Task = std::function<void()>;
145 using TimePoint = std::chrono::steady_clock::time_point;
146 using Duration = std::chrono::steady_clock::duration;
164 : pool_(worker_threads), stop_(false), next_task_id_(1)
166 std::promise<Tid> scheduler_started;
167 auto scheduler_ready = scheduler_started.get_future();
169 scheduler_thread_ =
ThreadWrapper([
this, started = std::move(scheduler_started)]()
mutable {
174 scheduler_tid_ = scheduler_ready.get();
194 auto run_time = std::chrono::steady_clock::now() + delay;
198 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, Task>,
int> = 0>
201 auto run_time = std::chrono::steady_clock::now() + delay;
216 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, Task>,
int> = 0>
236 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, Task>,
int> = 0>
251 auto const run_time = std::chrono::steady_clock::now() + initial_delay;
255 template <
typename F, std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<F>, Task>,
int> = 0>
258 auto const run_time = std::chrono::steady_clock::now() + initial_delay;
259 return insert_periodic_task(run_time, interval,
279 std::lock_guard<std::mutex> lock(mutex_);
280 return scheduled_tasks_.size();
297 std::lock_guard<std::mutex> lock(mutex_);
303 condition_.notify_one();
305 if (scheduler_thread_.joinable())
307 scheduler_thread_.join();
321 return pool_.configure_threads(name_prefix, policy, priority);
326 if (!scheduler_thread_.joinable() || scheduler_tid_ ==
Tid{})
336 if (!info.has_value())
337 return unexpected(std::make_error_code(std::errc::no_such_process));
344 Tid scheduler_tid_{};
346 mutable std::mutex mutex_;
347 std::condition_variable condition_;
348 std::atomic<bool> stop_;
350 std::multimap<TimePoint, ScheduledTaskInfo> scheduled_tasks_;
351 std::atomic<uint64_t> next_task_id_;
355 std::lock_guard<std::mutex> lock(mutex_);
357 uint64_t
const task_id = next_task_id_++;
358 ScheduledTaskHandle handle(task_id);
368 info.next_run = run_time;
369 info.interval = Duration::zero();
370 info.one_shot_task = std::move(task);
371 info.cancelled = handle.get_cancel_flag();
372 info.periodic =
false;
374 scheduled_tasks_.insert({run_time, std::move(info)});
375 condition_.notify_one();
382 std::lock_guard<std::mutex> lock(mutex_);
384 uint64_t
const task_id = next_task_id_++;
385 ScheduledTaskHandle handle(task_id);
395 info.next_run = run_time;
396 info.interval = interval;
397 info.periodic_task = std::make_shared<PeriodicTask>(std::move(task));
398 info.cancelled = handle.get_cancel_flag();
399 info.periodic =
true;
401 scheduled_tasks_.insert({run_time, std::move(info)});
402 condition_.notify_one();
407 void scheduler_loop()
411 std::unique_lock<std::mutex> lock(mutex_);
414 if (scheduled_tasks_.empty())
416 condition_.wait(lock, [
this] {
return stop_ || !scheduled_tasks_.empty(); });
423 auto const now = std::chrono::steady_clock::now();
424 auto it = scheduled_tasks_.begin();
426 if (it == scheduled_tasks_.end())
434 condition_.wait_until(lock, it->first, [
this] { return stop_.load(); });
444 scheduled_tasks_.erase(it);
447 if (info.cancelled->load(std::memory_order_acquire))
455 auto cancelled_flag = info.cancelled;
458 auto periodic_task = info.periodic_task;
459 pool_.post([periodic_task, cancelled_flag]() {
460 if (!cancelled_flag->load(std::memory_order_acquire))
466 if (!info.cancelled->load(std::memory_order_acquire))
468 info.next_run += info.interval;
469 scheduled_tasks_.insert({info.next_run, std::move(info)});
474 auto one_shot_task = std::move(info.one_shot_task);
475 pool_.post([task = std::move(one_shot_task), cancelled_flag]()
mutable {
476 if (!cancelled_flag->load(std::memory_order_acquire))
Copyable handle for a cancellable scheduled task.
auto id() const noexcept -> uint64_t
auto is_cancelled() const noexcept -> bool
friend class ScheduledThreadPoolT
ScheduledTaskHandle(uint64_t id)
Thread pool augmented with delayed and periodic task scheduling.
auto schedule_at(TimePoint time_point, F &&task) -> ScheduledTaskHandle
auto scheduled_count() const -> size_t
Get number of scheduled tasks (including periodic)
std::function< void()> Task
void shutdown()
Shutdown the scheduler and wait for completion.
auto scheduler_thread_info() const -> std::optional< ThreadInfo >
auto schedule_periodic(Duration interval, F &&task) -> ScheduledTaskHandle
auto thread_pool() -> PoolType &
Get the underlying thread pool for direct task submission.
auto operator=(ScheduledThreadPoolT const &) -> ScheduledThreadPoolT &=delete
auto schedule_after(Duration delay, F &&task) -> ScheduledTaskHandle
ScheduledThreadPoolT(size_t worker_threads=std::thread::hardware_concurrency())
Create a scheduled thread pool.
auto schedule_periodic_after(Duration initial_delay, Duration interval, F &&task) -> ScheduledTaskHandle
static void cancel(ScheduledTaskHandle &handle)
Cancel a scheduled task by handle.
auto schedule_periodic_after(Duration initial_delay, Duration interval, Task task) -> ScheduledTaskHandle
Schedule a task to run periodically after an initial delay.
std::chrono::steady_clock::time_point TimePoint
std::chrono::steady_clock::duration Duration
auto configure_scheduler_thread(std::string const &name, SchedulingPolicy policy=SchedulingPolicy::OTHER, ThreadPriority priority=ThreadPriority::normal()) -> expected< void, std::error_code >
ScheduledThreadPoolT(ScheduledThreadPoolT const &)=delete
auto schedule_after(Duration delay, Task task) -> ScheduledTaskHandle
Schedule a task to run after a delay.
detail::copyable_callable< void()> PeriodicTask
auto configure_threads(std::string const &name_prefix, SchedulingPolicy policy=SchedulingPolicy::OTHER, ThreadPriority priority=ThreadPriority::normal())
Configure worker threads.
detail::move_callable< void()> OneShotTask
auto schedule_at(TimePoint time_point, Task task) -> ScheduledTaskHandle
Schedule a task to run at a specific time point.
auto schedule_periodic(Duration interval, Task task) -> ScheduledTaskHandle
Schedule a task to run periodically at fixed intervals.
Lightweight handle for querying and controlling a specific OS thread.
static auto get_thread_id() -> Tid
auto set_name(std::string const &name) const -> expected< void, std::error_code >
Value-semantic wrapper for a thread scheduling priority.
static constexpr auto normal() noexcept -> ThreadPriority
Owning wrapper around std::thread with RAII join-on-destroy semantics.
A result type that holds either a value of type T or an error of type E.
Exception thrown by expected::value() when the object is in the error state.
Polyfill for std::expected (C++23) for pre-C++23 compilers.
std::function< Signature > copyable_callable
auto make_move_callable(Callable &&callable) -> move_callable< Signature >
auto configure_thread(ThreadLike &thread, std::string const &name, SchedulingPolicy policy, ThreadPriority priority) -> expected< void, std::error_code >
std::function< Signature > move_callable
auto make_copyable_callable(Callable &&callable) -> copyable_callable< Signature >
SchedulingPolicy
Enumeration of available thread scheduling policies.
@ OTHER
Standard round-robin time-sharing.
ScheduledThreadPoolT< ThreadPool > ScheduledThreadPool
ScheduledThreadPoolT using the default ThreadPool backend.
ScheduledThreadPoolT< FastThreadPool > ScheduledFastThreadPool
ScheduledThreadPoolT using FastThreadPool as backend.
ScheduledThreadPoolT< LightweightPool > ScheduledLightweightPool
ScheduledThreadPoolT using LightweightPool as backend (minimal overhead).
ScheduledThreadPoolT< HighPerformancePool > ScheduledHighPerformancePool
ScheduledThreadPoolT using HighPerformancePool as backend.
OneShotTask one_shot_task
std::shared_ptr< std::atomic< bool > > cancelled
std::shared_ptr< PeriodicTask > periodic_task
Thread pools: HighPerformancePool, ThreadPoolBase, LightweightPoolT, and GlobalPool.