1use std::sync::{
5 Arc,
6 atomic::{AtomicBool, Ordering},
7};
8
9use reifydb_value::value::duration::Duration;
10
11#[cfg(all(not(reifydb_single_threaded), not(loom)))]
12use crate::actor::timers::Repeat;
13#[cfg(reifydb_dst)]
14use crate::actor::timers::dst as dst_timers;
15#[cfg(reifydb_target = "wasi")]
16use crate::actor::timers::wasi::{schedule_once_fn, schedule_repeat, schedule_repeat_fn};
17#[cfg(reifydb_target = "wasm")]
18use crate::actor::timers::wasm::{schedule_once_fn, schedule_repeat, schedule_repeat_fn};
19use crate::actor::{mailbox::ActorRef, system::ActorSystem, timers::TimerHandle};
20
21#[derive(Clone)]
22pub struct CancellationToken {
23 cancelled: Arc<AtomicBool>,
24 parent: Option<Arc<AtomicBool>>,
25}
26
27impl CancellationToken {
28 pub fn new() -> Self {
29 Self {
30 cancelled: Arc::new(AtomicBool::new(false)),
31 parent: None,
32 }
33 }
34
35 pub fn child_token(&self) -> Self {
36 Self {
37 cancelled: Arc::new(AtomicBool::new(false)),
38 parent: Some(Arc::clone(&self.cancelled)),
39 }
40 }
41
42 pub fn cancel(&self) {
43 self.cancelled.store(true, Ordering::SeqCst);
44 }
45
46 pub fn is_cancelled(&self) -> bool {
47 self.cancelled.load(Ordering::SeqCst) || self.parent.as_ref().is_some_and(|p| p.load(Ordering::SeqCst))
48 }
49}
50
51impl Default for CancellationToken {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57pub struct Context<M> {
58 self_ref: ActorRef<M>,
59 system: ActorSystem,
60 cancel: CancellationToken,
61}
62
63impl<M: Send + 'static> Context<M> {
64 pub fn new(self_ref: ActorRef<M>, system: ActorSystem, cancel: CancellationToken) -> Self {
65 Self {
66 self_ref,
67 system,
68 cancel,
69 }
70 }
71
72 pub fn self_ref(&self) -> ActorRef<M> {
73 self.self_ref.clone()
74 }
75
76 pub fn system(&self) -> &ActorSystem {
77 &self.system
78 }
79
80 pub fn is_cancelled(&self) -> bool {
81 self.cancel.is_cancelled()
82 }
83
84 pub fn cancellation_token(&self) -> CancellationToken {
85 self.cancel.clone()
86 }
87}
88
89impl<M: Send + 'static> Context<M> {
90 #[cfg(all(not(reifydb_single_threaded), not(loom)))]
91 pub fn schedule_once<F: FnOnce() -> M + Send + 'static>(
92 &self,
93 delay: impl Into<Duration>,
94 factory: F,
95 ) -> TimerHandle {
96 let delay = delay.into().to_std();
97 let actor_ref = self.self_ref.clone();
98 self.system.scheduler().schedule_once(delay, move || {
99 let _ = actor_ref.send(factory());
100 })
101 }
102
103 #[cfg(all(loom, not(reifydb_single_threaded)))]
104 pub fn schedule_once<F: FnOnce() -> M + Send + 'static>(
105 &self,
106 _delay: impl Into<Duration>,
107 _factory: F,
108 ) -> TimerHandle {
109 panic!("actors never run under the loom backend, so no timer can fire")
110 }
111
112 #[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
113 pub fn schedule_once<F: FnOnce() -> M + Send + 'static>(
114 &self,
115 delay: impl Into<Duration>,
116 factory: F,
117 ) -> TimerHandle {
118 let delay = delay.into().to_std();
119 schedule_once_fn(self.self_ref.clone(), delay, factory)
120 }
121
122 #[cfg(reifydb_dst)]
123 pub fn schedule_once<F: FnOnce() -> M + Send + 'static>(
124 &self,
125 delay: impl Into<Duration>,
126 factory: F,
127 ) -> TimerHandle {
128 let delay = delay.into().to_std();
129 dst_timers::schedule_once_fn(
130 self.system.timer_heap(),
131 self.system.mock_clock(),
132 self.self_ref.clone(),
133 delay,
134 factory,
135 )
136 }
137}
138
139impl<M: Send + Sync + Clone + 'static> Context<M> {
140 #[cfg(all(not(reifydb_single_threaded), not(loom)))]
141 pub fn schedule_repeat(&self, interval: impl Into<Duration>, msg: M) -> TimerHandle {
142 let interval = interval.into().to_std();
143 let actor_ref = self.self_ref.clone();
144 self.system
145 .scheduler()
146 .schedule_repeat(interval, move || Repeat::after_send(actor_ref.send(msg.clone())))
147 }
148
149 #[cfg(all(loom, not(reifydb_single_threaded)))]
150 pub fn schedule_repeat(&self, _interval: impl Into<Duration>, _msg: M) -> TimerHandle {
151 panic!("actors never run under the loom backend, so no timer can fire")
152 }
153
154 #[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
155 pub fn schedule_repeat(&self, interval: impl Into<Duration>, msg: M) -> TimerHandle {
156 let interval = interval.into().to_std();
157 schedule_repeat(self.self_ref.clone(), interval, msg)
158 }
159
160 #[cfg(reifydb_dst)]
161 pub fn schedule_repeat(&self, interval: impl Into<Duration>, msg: M) -> TimerHandle {
162 let interval = interval.into().to_std();
163 dst_timers::schedule_repeat(
164 self.system.timer_heap(),
165 self.system.mock_clock(),
166 self.self_ref.clone(),
167 interval,
168 msg,
169 )
170 }
171}
172
173impl<M: Send + 'static> Context<M> {
174 #[cfg(all(not(reifydb_single_threaded), not(loom)))]
175 pub fn schedule_repeat_fn<F: Fn() -> M + Send + Sync + 'static>(
176 &self,
177 interval: impl Into<Duration>,
178 factory: F,
179 ) -> TimerHandle {
180 let interval = interval.into().to_std();
181 let actor_ref = self.self_ref.clone();
182 self.system.scheduler().schedule_repeat(interval, move || Repeat::after_send(actor_ref.send(factory())))
183 }
184
185 #[cfg(all(loom, not(reifydb_single_threaded)))]
186 pub fn schedule_repeat_fn<F: Fn() -> M + Send + Sync + 'static>(
187 &self,
188 _interval: impl Into<Duration>,
189 _factory: F,
190 ) -> TimerHandle {
191 panic!("actors never run under the loom backend, so no timer can fire")
192 }
193
194 #[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
195 pub fn schedule_repeat_fn<F: Fn() -> M + Send + Sync + 'static>(
196 &self,
197 interval: impl Into<Duration>,
198 factory: F,
199 ) -> TimerHandle {
200 let interval = interval.into().to_std();
201 schedule_repeat_fn(self.self_ref.clone(), interval, factory)
202 }
203
204 #[cfg(reifydb_dst)]
205 pub fn schedule_repeat_fn<F: Fn() -> M + Send + Sync + 'static>(
206 &self,
207 interval: impl Into<Duration>,
208 factory: F,
209 ) -> TimerHandle {
210 let interval = interval.into().to_std();
211 dst_timers::schedule_repeat_fn(
212 self.system.timer_heap(),
213 self.system.mock_clock(),
214 self.self_ref.clone(),
215 interval,
216 factory,
217 )
218 }
219
220 #[cfg(not(all(loom, not(reifydb_single_threaded))))]
221 pub fn schedule_tick<F: Fn(i64) -> M + Send + Sync + 'static>(
222 &self,
223 interval: impl Into<Duration>,
224 factory: F,
225 ) -> TimerHandle {
226 let interval = interval.into().to_std();
227 let actor_ref = self.self_ref.clone();
228 let clock = self.system.clock().clone();
229
230 #[cfg(not(reifydb_single_threaded))]
231 {
232 self.system.scheduler().schedule_repeat(interval, move || {
233 let now = clock.now().to_nanos();
234 Repeat::after_send(actor_ref.send(factory(now)))
235 })
236 }
237
238 #[cfg(all(reifydb_single_threaded, not(reifydb_dst)))]
239 {
240 schedule_repeat_fn(actor_ref, interval, move || {
241 let now = clock.now().to_nanos();
242 factory(now)
243 })
244 }
245
246 #[cfg(reifydb_dst)]
247 {
248 dst_timers::schedule_repeat_fn(
249 self.system.timer_heap(),
250 self.system.mock_clock(),
251 actor_ref,
252 interval,
253 move || {
254 let now = clock.now().to_nanos();
255 factory(now)
256 },
257 )
258 }
259 }
260
261 #[cfg(all(loom, not(reifydb_single_threaded)))]
262 pub fn schedule_tick<F: Fn(i64) -> M + Send + Sync + 'static>(
263 &self,
264 _interval: impl Into<Duration>,
265 _factory: F,
266 ) -> TimerHandle {
267 panic!("actors never run under the loom backend, so no timer can fire")
268 }
269}
270
271impl<M> Clone for Context<M> {
272 fn clone(&self) -> Self {
273 Self {
274 self_ref: self.self_ref.clone(),
275 system: self.system.clone(),
276 cancel: self.cancel.clone(),
277 }
278 }
279}