Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
scheduler.hpp
Go to the documentation of this file.
1#pragma once
2
3// IScheduler — unified scheduling base.
4//
5// Aria has historically grown three sibling abstractions:
6//
7// * `aria::async::IExecutor` — "post(fn)"; used by coroutines /
8// async_command / graph executor.
9// * `aria::runtime::IDispatcher` — "post(fn) + post_delayed + pump +
10// is_main_thread"; the platform
11// UI/main-thread dispatcher.
12// * `aria::IDelayedScheduler` — "post_after(delay, fn)"; the tiny
13// timer interface used by debounce
14// / throttle / retry / with_timeout.
15//
16// Each grew its own naming, its own role, and its own capability story.
17// `IScheduler` is the single inheritance root that lets every component
18// in the framework reason about "what can this thing do?" through a
19// stable, declarative bitmask — without breaking any existing concrete
20// class hierarchy.
21//
22// ┌────────────────────────────┐
23// │ IScheduler │ caps(): SchedulerCaps bitmask
24// │ schedule(fn) │ pure virtual — submit a callable
25// │ schedule_after(...) │ default: throws unsupported_capability
26// └─────────────┬──────────────┘
27// │
28// ┌─────────────┼─────────────────────────────┐
29// │ │ │
30// IDelayedScheduler IExecutor IDispatcher
31// (Caps::Delay) (Caps::Post + GraphSafe? (Caps::Post |
32// + WorkerSafe?) Caps::Delay |
33// Caps::MainThread |
34// Caps::Pumpable)
35//
36// All three derived interfaces inherit from `IScheduler` virtually so
37// that `VirtualTimeExecutor : IExecutor, IDelayedScheduler` (and any
38// future multi-role implementation) collapses to a single IScheduler
39// subobject without ambiguity.
40//
41// The contract is intentionally minimal: `schedule(fn)` is the only
42// universally available operation. Anything else (delay, main-thread
43// affinity, pumping, pool-style worker hosting) is a CAPABILITY that
44// the implementation declares via `caps()`. Callers query the bitmask
45// once and either (a) use a richer interface via `dynamic_cast`, or
46// (b) gracefully degrade.
47//
48// USAGE FROM A COMPONENT THAT NEEDS A TIMER:
49//
50// void wire_debounce(IScheduler& s, ...) {
51// if (!has_caps(s, SchedulerCaps::Delay))
52// throw std::logic_error("scheduler has no Delay capability");
53// s.schedule_after(300ms, []{ ... }); // safe — caps() said yes
54// }
55//
56// USAGE FROM A COMPONENT THAT REQUIRES A PUMPABLE MAIN-THREAD QUEUE:
57//
58// if (!has_caps(s, SchedulerCaps::MainThread | SchedulerCaps::Pumpable))
59// throw ...;
60//
61// IMPORTANT: This header introduces *no* breaking change. Every
62// pre-existing concrete class (`ThreadPoolExecutor`, `MainThreadExecutor`,
63// `InlineExecutor`, `SimpleDispatcher`, `VirtualTimeExecutor`, …) is
64// retrofitted with a `caps()` override that declares its capabilities.
65// Existing call sites keep using the rich interfaces directly.
66
67#include "aria/abi/export.hpp"
68
69#include <chrono>
70#include <cstdint>
71#include <functional>
72#include <stdexcept>
73#include <type_traits>
74
75namespace aria {
76
77// ────────────────────────────────────────────────────────────────────────
78// Capability bitmask
79//
80// `enum class : std::uint32_t` so it can be stored as a packed atomic
81// or a struct member without ABI surprises.
82// ────────────────────────────────────────────────────────────────────────
83enum class SchedulerCaps : std::uint32_t {
84 None = 0,
85
89 Post = 1u << 0,
90
93 Delay = 1u << 1,
94
97 MainThread = 1u << 2,
98
102 Pumpable = 1u << 3,
103
107 GraphSafe = 1u << 4,
108
111 WorkerSafe = 1u << 5,
112
116 Autonomous = 1u << 6,
117};
118
119[[nodiscard]] constexpr SchedulerCaps operator|(SchedulerCaps a, SchedulerCaps b) noexcept {
120 return static_cast<SchedulerCaps>(static_cast<std::uint32_t>(a) | static_cast<std::uint32_t>(b));
121}
122[[nodiscard]] constexpr SchedulerCaps operator&(SchedulerCaps a, SchedulerCaps b) noexcept {
123 return static_cast<SchedulerCaps>(static_cast<std::uint32_t>(a) & static_cast<std::uint32_t>(b));
124}
126 a = a | b; return a;
127}
129 a = a & b; return a;
130}
131[[nodiscard]] constexpr bool has_any(SchedulerCaps a, SchedulerCaps b) noexcept {
132 return static_cast<std::uint32_t>(a & b) != 0;
133}
134[[nodiscard]] constexpr bool has_all(SchedulerCaps a, SchedulerCaps b) noexcept {
135 return (a & b) == b;
136}
137
138// ────────────────────────────────────────────────────────────────────────
139// Exceptions
140// ────────────────────────────────────────────────────────────────────────
141
144class unsupported_capability : public std::logic_error {
145public:
146 using std::logic_error::logic_error;
147};
148
149// ────────────────────────────────────────────────────────────────────────
150// IScheduler
151// ────────────────────────────────────────────────────────────────────────
153public:
154 virtual ~IScheduler() = default;
155
158 [[nodiscard]] virtual SchedulerCaps caps() const noexcept = 0;
159
161 virtual void schedule(std::function<void()> fn) = 0;
162
166 virtual void schedule_after(std::chrono::milliseconds delay,
167 std::function<void()> fn) {
168 (void)delay; (void)fn;
170 "IScheduler::schedule_after: this scheduler does not advertise "
171 "SchedulerCaps::Delay");
172 }
173
177 [[nodiscard]] virtual bool is_main_thread() const noexcept { return false; }
178};
179
180// ────────────────────────────────────────────────────────────────────────
181// Free helpers
182// ────────────────────────────────────────────────────────────────────────
183
184[[nodiscard]] inline bool has_caps(const IScheduler& s, SchedulerCaps required) noexcept {
185 return has_all(s.caps(), required);
186}
187
190inline void require_caps(const IScheduler& s, SchedulerCaps required,
191 const char* context = "scheduler") {
192 if (!has_caps(s, required)) {
193 throw unsupported_capability(std::string{context}
194 + ": required SchedulerCaps not advertised by this scheduler");
195 }
196}
197
198} // namespace aria
Definition scheduler.hpp:152
virtual void schedule(std::function< void()> fn)=0
Submit fn for execution "soon". Defines Caps::Post.
virtual SchedulerCaps caps() const noexcept=0
Capability bitmask.
virtual bool is_main_thread() const noexcept
True iff the calling thread is the scheduler's "main" thread.
Definition scheduler.hpp:177
virtual ~IScheduler()=default
virtual void schedule_after(std::chrono::milliseconds delay, std::function< void()> fn)
Submit fn for execution after delay.
Definition scheduler.hpp:166
Thrown when a caller invokes a capability the scheduler did not advertise (e.g.
Definition scheduler.hpp:144
#define ARIA_ABI_API
Definition export.hpp:21
Definition signal.hpp:12
constexpr bool has_any(SchedulerCaps a, SchedulerCaps b) noexcept
Definition scheduler.hpp:131
constexpr SchedulerCaps operator&(SchedulerCaps a, SchedulerCaps b) noexcept
Definition scheduler.hpp:122
constexpr SchedulerCaps & operator&=(SchedulerCaps &a, SchedulerCaps b) noexcept
Definition scheduler.hpp:128
bool has_caps(const IScheduler &s, SchedulerCaps required) noexcept
Definition scheduler.hpp:184
constexpr bool has_all(SchedulerCaps a, SchedulerCaps b) noexcept
Definition scheduler.hpp:134
constexpr SchedulerCaps & operator|=(SchedulerCaps &a, SchedulerCaps b) noexcept
Definition scheduler.hpp:125
void require_caps(const IScheduler &s, SchedulerCaps required, const char *context="scheduler")
Throwing accessor — useful at component construction time when missing a capability is a programmer e...
Definition scheduler.hpp:190
SchedulerCaps
Definition scheduler.hpp:83
@ Post
Can submit "fire now" work.
Definition scheduler.hpp:89
@ GraphSafe
Safe to use as the graph-thread executor — i.e.
Definition scheduler.hpp:107
@ Pumpable
Posted work is held in a queue until a pump-style call drains it (e.g.
Definition scheduler.hpp:102
@ None
Definition scheduler.hpp:84
@ Autonomous
Implementation does not require any external pump and runs work on background threads autonomously.
Definition scheduler.hpp:116
@ Delay
Can submit work after a wall-clock or virtual-time delay.
Definition scheduler.hpp:93
@ MainThread
Submitted work runs on a single, identifiable "main" thread that is consistent across calls.
Definition scheduler.hpp:97
@ WorkerSafe
Safe to host blocking worker tasks (e.g.
Definition scheduler.hpp:111
constexpr SchedulerCaps operator|(SchedulerCaps a, SchedulerCaps b) noexcept
Definition scheduler.hpp:119
Definition validation_key.hpp:110