ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
scheduled/facade.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "../../result.hpp"
4#include "../../scheduled_task.hpp"
5#include "../../shutdown_policy.hpp"
6#include "../../thread_config.hpp"
7#include "../../worker_count.hpp"
8#include "../../worker_registration.hpp"
9#include "../pool/shutdown.hpp"
10#include "../thread/control.hpp"
11#include "../time.hpp"
12
13#include <atomic>
14#include <chrono>
15#include <cstddef>
16#include <functional>
17#include <memory>
18#include <system_error>
19#include <thread>
20#include <utility>
21
23{
24
25template <typename Backend>
27{
28public:
30 : impl_(std::make_unique<Backend>(count.resolve(), registration == worker_registration::global_registry))
31 {
32 }
33
38
39 template <typename Rep, typename Period, typename F>
40 auto
41 schedule_after(std::chrono::duration<Rep, Period> delay, F&& function) -> result<scheduled_task>
42 {
43 if (stopped_.load(std::memory_order_acquire))
44 return unexpected(std::make_error_code(std::errc::operation_canceled));
45 return try_result(
47 {
48 auto native_delay = checked_duration_cast<typename Backend::duration>(delay);
49 if (!native_delay)
50 return unexpected(native_delay.error());
51 auto handle = impl_->schedule_after(native_delay.value(), std::forward<F>(function));
52 if (handle.is_cancelled())
53 return unexpected(std::make_error_code(std::errc::operation_canceled));
54 return scheduled_task_access::make(std::move(handle));
55 });
56 }
57
58 template <typename F>
59 auto
60 schedule_at(std::chrono::steady_clock::time_point time, F&& function) -> result<scheduled_task>
61 {
62 if (stopped_.load(std::memory_order_acquire))
63 return unexpected(std::make_error_code(std::errc::operation_canceled));
64 return try_result(
66 {
67 auto handle = impl_->schedule_at(time, std::forward<F>(function));
68 if (handle.is_cancelled())
69 return unexpected(std::make_error_code(std::errc::operation_canceled));
70 return scheduled_task_access::make(std::move(handle));
71 });
72 }
73
74 template <typename Rep, typename Period, typename F>
75 auto
76 schedule_periodic(std::chrono::duration<Rep, Period> interval, F&& function) -> result<scheduled_task>
77 {
78 auto const native_interval = checked_duration_cast<typename Backend::duration>(interval);
79 if (!native_interval)
80 return unexpected(native_interval.error());
81 if (native_interval.value() <= Backend::duration::zero())
82 return unexpected(std::make_error_code(std::errc::invalid_argument));
83 if (stopped_.load(std::memory_order_acquire))
84 return unexpected(std::make_error_code(std::errc::operation_canceled));
85 return try_result(
87 {
88 auto handle = impl_->schedule_periodic(native_interval.value(), std::forward<F>(function));
89 if (handle.is_cancelled())
90 return unexpected(std::make_error_code(std::errc::operation_canceled));
91 return scheduled_task_access::make(std::move(handle));
92 });
93 }
94
95 template <typename InitialRep, typename InitialPeriod, typename IntervalRep, typename IntervalPeriod, typename F>
96 auto
97 schedule_periodic_after(std::chrono::duration<InitialRep, InitialPeriod> initial_delay,
98 std::chrono::duration<IntervalRep, IntervalPeriod> interval, F&& function)
100 {
101 auto const native_interval = checked_duration_cast<typename Backend::duration>(interval);
102 if (!native_interval)
103 return unexpected(native_interval.error());
104 if (native_interval.value() <= Backend::duration::zero())
105 return unexpected(std::make_error_code(std::errc::invalid_argument));
106 auto const native_delay = checked_duration_cast<typename Backend::duration>(initial_delay);
107 if (!native_delay)
108 return unexpected(native_delay.error());
109 if (stopped_.load(std::memory_order_acquire))
110 return unexpected(std::make_error_code(std::errc::operation_canceled));
111 return try_result(
113 {
114 auto handle = impl_->schedule_periodic_after(native_delay.value(), native_interval.value(),
115 std::forward<F>(function));
116 if (handle.is_cancelled())
117 return unexpected(std::make_error_code(std::errc::operation_canceled));
118 return scheduled_task_access::make(std::move(handle));
119 });
120 }
121
122 auto
124 {
125 if (!impl_)
126 return unexpected(std::make_error_code(std::errc::operation_canceled));
127 return try_result([&]() -> result<void> { return impl_->configure_threads(to_native(config)); });
128 }
129
130 auto
132 {
133 if (!impl_)
134 return unexpected(std::make_error_code(std::errc::operation_canceled));
135 return try_result([&]() -> result<void> { return impl_->configure_scheduler_thread(to_native(config)); });
136 }
137
138 auto
140 {
141 if (!impl_)
142 return {};
143 return try_result(
144 [&]() -> result<void>
145 {
146 impl_->shutdown(to_native(policy));
147 stopped_.store(true, std::memory_order_release);
148 return {};
149 });
150 }
151
152 [[nodiscard]] auto
153 scheduled_count() const -> std::size_t
154 {
155 return impl_ ? impl_->scheduled_count() : 0;
156 }
157
158protected:
160 {
161 dispose_impl();
162 }
163
164private:
165 void
166 dispose_impl() noexcept
167 {
168 if (!impl_)
169 return;
170
171 if (!impl_->is_current_context())
172 {
173 try
174 {
175 impl_->shutdown(shutdown_policy_backend::drain);
176 }
177 catch (...)
178 {
179 }
180 impl_.reset();
181 return;
182 }
183
184 auto* backend = impl_.release();
185 try
186 {
187 std::thread reaper(
188 [backend]() noexcept
189 {
190 std::unique_ptr<Backend> owner(backend);
191 try
192 {
193 owner->shutdown(shutdown_policy_backend::drain);
194 }
195 catch (...)
196 {
197 }
198 });
199 reaper.detach();
200 }
201 catch (...)
202 {
203 (void)backend;
204 }
205 }
206
207 std::unique_ptr<Backend> impl_;
208 std::atomic<bool> stopped_{ false };
209};
210
211} // namespace threadschedule::detail
scheduled_pool_facade(scheduled_pool_facade &&)=delete
auto configure_workers(thread_config const &config) -> result< void >
auto shutdown(shutdown_policy policy=shutdown_policy::drain) -> result< void >
auto schedule_at(std::chrono::steady_clock::time_point time, F &&function) -> result< scheduled_task >
auto schedule_periodic_after(std::chrono::duration< InitialRep, InitialPeriod > initial_delay, std::chrono::duration< IntervalRep, IntervalPeriod > interval, F &&function) -> result< scheduled_task >
auto configure_scheduler(thread_config const &config) -> result< void >
scheduled_pool_facade(worker_count count, worker_registration registration)
auto operator=(scheduled_pool_facade const &) -> scheduled_pool_facade &=delete
auto schedule_periodic(std::chrono::duration< Rep, Period > interval, F &&function) -> result< scheduled_task >
auto operator=(scheduled_pool_facade &&) -> scheduled_pool_facade &=delete
auto schedule_after(std::chrono::duration< Rep, Period > delay, F &&function) -> result< scheduled_task >
scheduled_pool_facade(scheduled_pool_facade const &)=delete
Portable thread configuration bundle.
Value type for pool worker count.
Aggregates multiple thread_registry_backend instances into a single queryable view.
constexpr auto to_native(shutdown_policy policy) noexcept -> shutdown_policy_backend
Definition shutdown.hpp:10
auto try_result(Function &&function) -> decltype(std::forward< Function >(function)())
auto global_registry() -> thread_registry &
shutdown_policy
Defines how pools handle pending work during shutdown.
@ drain
Finish queued work before returning from shutdown.
worker_registration
Controls whether pool workers are registered in a thread registry.