dope_runtime/executor/
runner.rs1use std::{
2 future::Future,
3 ptr::NonNull,
4 task::{Context, Poll, Waker},
5 time::Instant,
6};
7
8use dope_core::driver::{CompletionBackend, CurrentSlot, DriverLifecycle, PoolDriver};
9use dope_core::profile::{Profile, Throughput};
10
11use super::ctx::{RuntimeCtx, RuntimeHandles};
12use super::external::{ExternalRouter, current_external};
13use super::join_handle::JoinHandle;
14use super::{LocalExecutor, run_ready};
15use crate::runtime::Runtime;
16
17impl<B, P> Runtime<LocalExecutor<B, P>>
18where
19 B: PoolDriver,
20 P: Profile,
21{
22 pub fn spawn<F>(&self, fut: F) -> JoinHandle<F::Output>
23 where
24 F: Future + 'static,
25 F::Output: 'static,
26 {
27 self.reactor.exec.spawn(fut)
28 }
29
30 pub fn ctx(&mut self) -> RuntimeCtx<B> {
31 let driver_ptr = NonNull::from(&mut self.driver);
32 let extra = RuntimeHandles {
33 exec: NonNull::from(&*self.reactor.exec),
34 timers: NonNull::from(&*self.reactor.timers),
35 };
36 let extra_ptr = Box::leak(Box::new(extra)) as *const RuntimeHandles as *const ();
37 RuntimeCtx::from_slot(CurrentSlot::<B>::new(driver_ptr, extra_ptr))
38 }
39
40 pub fn block_on<F>(&mut self, fut: F) -> F::Output
41 where
42 F: Future,
43 {
44 self.run_until(fut)
45 }
46
47 pub fn run_until<F>(&mut self, fut: F) -> F::Output
48 where
49 F: Future,
50 {
51 let mut fut = std::pin::pin!(fut);
52 let waker = Waker::noop();
53 let mut cx = Context::from_waker(waker);
54
55 let Self {
56 reactor, driver, ..
57 } = self;
58 let extra = RuntimeHandles {
59 exec: NonNull::from(&*reactor.exec),
60 timers: NonNull::from(&*reactor.timers),
61 };
62 let raw = NonNull::from(&mut *driver);
63 let slot = CurrentSlot::<B>::new(raw, &extra as *const _ as *const ());
64 let _guard = SlotGuard::<B>::install(slot);
65
66 loop {
67 reactor.timers.wake_due();
68 run_ready::<P>(&reactor.exec);
69
70 if let Poll::Ready(v) = fut.as_mut().poll(&mut cx) {
71 return v;
72 }
73
74 let aux = current_external::<B>();
75
76 let progressed = matches!(
77 B::drive_with(driver, &mut ExternalRouter::<B> { aux }),
78 Ok(true)
79 );
80 if let Some(a) = aux {
81 unsafe { (a.pump)(a.state, driver) };
82 }
83
84 if progressed {
85 reactor.timers.wake_due();
86 run_ready::<P>(&reactor.exec);
87 if let Poll::Ready(v) = fut.as_mut().poll(&mut cx) {
88 return v;
89 }
90 }
91
92 if !reactor.exec.is_empty() {
93 continue;
94 }
95 for _ in 0..4 {
96 if let Some(a) = aux {
97 unsafe { (a.pump)(a.state, driver) };
98 }
99 reactor.timers.wake_due();
100 run_ready::<P>(&reactor.exec);
101 if let Poll::Ready(v) = fut.as_mut().poll(&mut cx) {
102 return v;
103 }
104 if !reactor.exec.is_empty() {
105 break;
106 }
107 if !driver.has_pending() {
108 break;
109 }
110 }
111 if !reactor.exec.is_empty() {
112 continue;
113 }
114 let now = Instant::now();
115 if let Some(deadline) = reactor.timers.next_deadline() {
116 let _ = driver.park_for(deadline.saturating_duration_since(now));
117 } else if let Some(timeout) = P::IDLE_PARK {
118 let _ = driver.park_for(timeout);
119 } else {
120 let _ = driver.park();
121 }
122 }
123 }
124
125 pub fn shutdown(&mut self) {
126 let Self {
127 reactor, driver, ..
128 } = self;
129 let extra = RuntimeHandles {
130 exec: NonNull::from(&*reactor.exec),
131 timers: NonNull::from(&*reactor.timers),
132 };
133 let raw = NonNull::from(&mut *driver);
134 let slot = CurrentSlot::<B>::new(raw, &extra as *const _ as *const ());
135 let _guard = SlotGuard::<B>::install(slot);
136
137 let mut last_activity = Instant::now();
138 loop {
139 reactor.timers.wake_due();
140 let ran_ready = run_ready::<P>(&reactor.exec);
141 if ran_ready > 0 {
142 last_activity = Instant::now();
143 }
144 let progressed = matches!(driver.drive(), Ok(true));
145 if (ran_ready > 0 || progressed) && driver.has_pending() {
146 continue;
147 }
148
149 let next_deadline = reactor.timers.next_deadline();
150 if reactor.exec.is_empty() && next_deadline.is_none() {
151 break;
152 }
153
154 let now = Instant::now();
155 let timeout =
156 reactor
157 .idle_park_policy
158 .timeout_for(now, next_deadline, None, last_activity);
159 let Some(timeout) = timeout else {
160 break;
161 };
162 let _ = driver.park_for(timeout);
163 }
164 }
165}
166
167pub fn block_on<B, F>(fut: F) -> F::Output
168where
169 B: PoolDriver,
170 F: Future,
171{
172 let mut rt = Runtime::<LocalExecutor<B, Throughput>>::new_local()
173 .expect("dope_runtime::block_on: failed to construct runtime");
174 let out = rt.run_until(fut);
175 rt.shutdown();
176 out
177}
178
179pub fn current<B: CompletionBackend>() -> RuntimeCtx<B> {
180 let slot = B::current_slot()
181 .expect("dope_runtime runtime invariant violated: current runtime ctx missing");
182 RuntimeCtx::from_slot(slot)
183}
184
185pub fn spawn<B, F>(fut: F) -> JoinHandle<F::Output>
186where
187 B: PoolDriver,
188 F: Future + 'static,
189 F::Output: 'static,
190{
191 current::<B>().spawn(fut)
192}
193
194pub(super) struct SlotGuard<B: CompletionBackend>(std::marker::PhantomData<B>);
195
196impl<B: CompletionBackend> SlotGuard<B> {
197 pub(super) fn install(slot: CurrentSlot<B>) -> Self {
198 let prev = B::current_slot();
199 assert!(
200 prev.is_none(),
201 "dope_runtime runtime invariant violated: runtime slot already set"
202 );
203 B::set_slot(Some(slot));
204 Self(std::marker::PhantomData)
205 }
206}
207
208impl<B: CompletionBackend> Drop for SlotGuard<B> {
209 fn drop(&mut self) {
210 B::set_slot(None);
211 }
212}