1use std::sync::{Arc, Mutex, PoisonError};
14use std::task::{Context, Poll, Waker};
15use std::thread::{self, JoinHandle};
16use std::time::{Duration, Instant};
17use std::vec::Vec;
18
19use crate::core::line::{CompletionLine, Slot, reached};
20use crate::core::reactor::Idle;
21use crate::core::time::{Clock, DurNs, MonoNs, Timer};
22
23#[derive(Clone, Copy, Debug)]
25pub struct StdClock {
26 origin: Instant,
27}
28
29impl StdClock {
30 #[must_use]
32 pub fn new() -> Self {
33 Self {
34 origin: Instant::now(),
35 }
36 }
37
38 #[must_use]
40 pub fn from_origin(origin: Instant) -> Self {
41 Self { origin }
42 }
43}
44
45impl Default for StdClock {
46 fn default() -> Self {
47 Self::new()
48 }
49}
50
51impl Clock for StdClock {
52 fn monotonic_ns(&self) -> MonoNs {
53 MonoNs(u64::try_from(self.origin.elapsed().as_nanos()).unwrap_or(u64::MAX))
54 }
55}
56
57impl Timer for StdClock {
58 async fn sleep_until(&self, at: MonoNs) {
59 let left = at.since(self.monotonic_ns());
60 if left.0 > 0 {
61 thread::sleep(Duration::from_nanos(left.0));
62 }
63 }
64}
65
66#[derive(Clone, Copy, Debug)]
68enum LinePark {
69 #[cfg(feature = "wordwait")]
70 Word(crate::backend::wordwait::WordWait),
71 Yield,
72}
73
74#[derive(Clone, Copy, Debug)]
76pub struct StdIdle {
77 clock: StdClock,
78 park: LinePark,
79}
80
81impl StdIdle {
82 #[must_use]
86 pub fn new(clock: StdClock) -> Self {
87 #[cfg(feature = "wordwait")]
88 if let Ok(w) = crate::backend::wordwait::WordWait::probe() {
89 return Self {
90 clock,
91 park: LinePark::Word(w),
92 };
93 }
94 Self {
95 clock,
96 park: LinePark::Yield,
97 }
98 }
99
100 #[cfg(feature = "wordwait")]
102 #[must_use]
103 pub fn with_word_wait(clock: StdClock, wait: crate::backend::wordwait::WordWait) -> Self {
104 Self {
105 clock,
106 park: LinePark::Word(wait),
107 }
108 }
109
110 #[must_use]
112 pub fn parks_in_hardware(&self) -> bool {
113 !matches!(self.park, LinePark::Yield)
114 }
115}
116
117impl Idle for StdIdle {
118 fn sleep_until(&mut self, at: MonoNs) {
119 let left = at.since(self.clock.monotonic_ns());
120 if left.0 > 0 {
121 thread::sleep(Duration::from_nanos(left.0));
122 }
123 }
124
125 fn park_line<R: Fn(&CompletionLine) -> bool>(
126 &mut self,
127 line: &CompletionLine,
128 ready: R,
129 bound: DurNs,
130 ) -> bool {
131 match self.park {
132 #[cfg(feature = "wordwait")]
133 LinePark::Word(w) => w.park(line, ready, Duration::from_nanos(bound.0)),
134 LinePark::Yield => {
135 let until = Instant::now() + Duration::from_nanos(bound.0);
136 while !ready(line) {
137 if Instant::now() >= until {
138 return false;
139 }
140 thread::yield_now();
141 }
142 true
143 }
144 }
145 }
146}
147
148fn line_of<L: AsRef<CompletionLine>>(l: &Arc<L>) -> &CompletionLine {
149 (**l).as_ref()
150}
151
152struct Entry<L> {
154 id: u64,
155 line: Arc<L>,
156 slot: Slot,
157 target: u64,
158 waker: Waker,
159}
160
161struct Table<L> {
163 next: u64,
164 entries: Vec<Entry<L>>,
165}
166
167struct Shared<L> {
168 table: Mutex<Table<L>>,
169 stop: std::sync::atomic::AtomicBool,
170}
171
172impl<L> Shared<L> {
173 fn lock(&self) -> std::sync::MutexGuard<'_, Table<L>> {
174 self.table.lock().unwrap_or_else(PoisonError::into_inner)
175 }
176}
177
178pub struct Watcher<L: AsRef<CompletionLine> + Send + Sync + 'static> {
181 shared: Arc<Shared<L>>,
182 thread: Option<JoinHandle<()>>,
183}
184
185impl<L: AsRef<CompletionLine> + Send + Sync + 'static> Watcher<L> {
186 #[must_use]
189 pub fn spawn(mut idle: StdIdle, park_bound: Duration) -> Self {
190 let shared = Arc::new(Shared {
191 table: Mutex::new(Table {
192 next: 0,
193 entries: Vec::new(),
194 }),
195 stop: std::sync::atomic::AtomicBool::new(false),
196 });
197 let s = Arc::clone(&shared);
198 let bound = DurNs(u64::try_from(park_bound.as_nanos()).unwrap_or(u64::MAX));
199 let thread = thread::spawn(move || {
200 while !s.stop.load(std::sync::atomic::Ordering::Acquire) {
201 let (done, first) = {
202 let mut t = s.lock();
203 let done: Vec<Entry<L>> = t
204 .entries
205 .extract_if(.., |x| reached(line_of(&x.line), x.slot, x.target))
206 .collect();
207 let first = t.entries.first().map(|x| (Arc::clone(&x.line), x.slot, x.target));
208 (done, first)
209 };
210 for e in done {
213 e.waker.wake();
214 }
215 match first {
216 None => thread::park(),
217 Some((line, slot, target)) => {
218 let _ = idle.park_line(
219 line_of(&line),
220 |l| reached(l, slot, target),
221 bound,
222 );
223 }
224 }
225 }
226 });
227 Self {
228 shared,
229 thread: Some(thread),
230 }
231 }
232
233 #[must_use]
235 pub fn wait(&self, line: Arc<L>, slot: Slot, target: u64) -> Watch<L> {
236 Watch {
237 shared: Arc::clone(&self.shared),
238 thread: self.thread.as_ref().map(|t| t.thread().clone()),
239 line,
240 slot,
241 target,
242 id: None,
243 }
244 }
245}
246
247impl<L: AsRef<CompletionLine> + Send + Sync + 'static> Drop for Watcher<L> {
248 fn drop(&mut self) {
249 self.shared
250 .stop
251 .store(true, std::sync::atomic::Ordering::Release);
252 if let Some(t) = self.thread.take() {
253 t.thread().unpark();
254 let _ = t.join();
255 }
256 }
257}
258
259pub struct Watch<L: AsRef<CompletionLine> + Send + Sync + 'static> {
262 shared: Arc<Shared<L>>,
263 thread: Option<thread::Thread>,
264 line: Arc<L>,
265 slot: Slot,
266 target: u64,
267 id: Option<u64>,
269}
270
271impl<L: AsRef<CompletionLine> + Send + Sync + 'static> Watch<L> {
272 fn reached(&self) -> bool {
273 reached(line_of(&self.line), self.slot, self.target)
274 }
275
276 fn deregister(&mut self) {
279 let Some(id) = self.id.take() else { return };
280 let removed = {
281 let mut t = self.shared.lock();
282 t.entries
283 .iter()
284 .position(|x| x.id == id)
285 .map(|i| t.entries.swap_remove(i))
286 };
287 drop(removed);
288 }
289}
290
291impl<L: AsRef<CompletionLine> + Send + Sync + 'static> Future for Watch<L> {
292 type Output = ();
293
294 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
295 let this = self.get_mut();
296 if this.reached() {
297 this.deregister();
298 return Poll::Ready(());
299 }
300 {
301 let mut t = this.shared.lock();
302 let mine = this.id.and_then(|id| t.entries.iter_mut().find(|x| x.id == id));
303 match mine {
304 Some(x) => x.waker.clone_from(cx.waker()),
305 None => {
307 let id = t.next;
308 t.next = t.next.wrapping_add(1);
309 t.entries.push(Entry {
310 id,
311 line: Arc::clone(&this.line),
312 slot: this.slot,
313 target: this.target,
314 waker: cx.waker().clone(),
315 });
316 this.id = Some(id);
317 }
318 }
319 }
320 if let Some(t) = &this.thread {
321 t.unpark();
322 }
323 if this.reached() {
326 this.deregister();
327 return Poll::Ready(());
328 }
329 Poll::Pending
330 }
331}
332
333impl<L: AsRef<CompletionLine> + Send + Sync + 'static> Drop for Watch<L> {
334 fn drop(&mut self) {
335 self.deregister();
336 }
337}