Skip to main content

dope_runtime/executor/
runner.rs

1use 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}