1use std::{cell::Cell, fmt, task::Waker};
3
4#[derive(Default)]
34pub struct LocalWaker {
35 waker: Cell<Option<ntex_rt::LocalWaker>>,
36}
37
38#[cfg(target_pointer_width = "64")]
39const _: () = assert!(size_of::<LocalWaker>() == 16);
40
41impl LocalWaker {
42 pub fn new() -> Self {
44 LocalWaker::with(None)
45 }
46
47 pub fn with(waker: Option<Waker>) -> Self {
49 LocalWaker {
50 waker: Cell::new(waker.map(ntex_rt::LocalWaker::from)),
51 }
52 }
53
54 #[inline]
55 pub fn register(&self, waker: &Waker) -> bool {
62 let prev = self.waker.take();
63 let registered = prev.is_some();
64 let same = prev.as_ref().is_some_and(|prev| prev.will_wake(waker));
65 self.waker.set(prev);
66 if !same {
67 let next = ntex_rt::LocalWaker::from_waker(waker);
70 drop(self.waker.replace(Some(next)));
71 }
72 registered
73 }
74
75 #[inline]
76 pub fn wake(&self) {
81 if let Some(waker) = self.take_local() {
82 waker.wake();
83 }
84 }
85
86 #[inline]
87 pub fn wake_checked(&self) -> bool {
92 if let Some(waker) = self.take_local() {
93 waker.wake();
94 true
95 } else {
96 false
97 }
98 }
99
100 pub fn take(&self) -> Option<Waker> {
106 self.take_local().map(ntex_rt::LocalWaker::into_waker)
107 }
108
109 #[inline]
114 pub fn take_local(&self) -> Option<ntex_rt::LocalWaker> {
115 self.waker.take()
116 }
117
118 #[doc(hidden)]
119 pub fn is_set(&self) -> bool {
121 let waker = self.waker.take();
122 let set = waker.is_some();
123 self.waker.set(waker);
124 set
125 }
126}
127
128impl Clone for LocalWaker {
130 fn clone(&self) -> Self {
131 LocalWaker::new()
132 }
133}
134
135impl fmt::Debug for LocalWaker {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "LocalWaker")
138 }
139}
140
141pub async fn yield_to() {
143 use std::{future::Future, pin::Pin, task::Context, task::Poll};
144
145 struct Yield {
146 completed: bool,
147 }
148
149 impl Future for Yield {
150 type Output = ();
151
152 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
153 if self.completed {
154 return Poll::Ready(());
155 }
156
157 self.completed = true;
158 cx.waker().wake_by_ref();
159
160 Poll::Pending
161 }
162 }
163
164 Yield { completed: false }.await;
165}
166
167#[cfg(test)]
168mod test {
169 use super::*;
170
171 #[ntex::test]
176 async fn yield_test() {
177 yield_to().await;
178 }
179
180 #[ntex::test]
186 async fn local_slot_wakes_current_task() {
187 crate::spawn(async {
188 let slot = LocalWaker::new();
189 let mut polls = 0;
190 std::future::poll_fn(|cx| {
191 polls += 1;
192 if polls == 4 {
193 return std::task::Poll::Ready(());
194 }
195 assert!(!slot.register(cx.waker()));
196 match polls {
197 1 => slot.wake(),
198 2 => slot.take_local().unwrap().wake(),
199 _ => slot.take().unwrap().wake(),
200 }
201 std::task::Poll::Pending
202 })
203 .await;
204 assert!(!slot.is_set());
205 })
206 .await
207 .unwrap();
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use std::panic::{AssertUnwindSafe, catch_unwind};
214 use std::rc::Rc;
215 use std::sync::Arc;
216 use std::sync::atomic::{AtomicUsize, Ordering};
217 use std::task::{RawWaker, RawWakerVTable, Wake};
218 use std::thread::LocalKey;
219
220 use super::*;
221
222 static CLONES: AtomicUsize = AtomicUsize::new(0);
223
224 static VTABLE: RawWakerVTable = RawWakerVTable::new(
225 |p| {
226 CLONES.fetch_add(1, Ordering::Relaxed);
227 RawWaker::new(p, &VTABLE)
228 },
229 |_| {},
230 |_| {},
231 |_| {},
232 );
233
234 type Hook = Cell<Option<Box<dyn FnOnce()>>>;
235
236 thread_local! {
238 static ON_CLONE: Hook = Cell::new(None);
239 static ON_DROP: Hook = Cell::new(None);
240 static ON_WAKE: Hook = Cell::new(None);
241 }
242
243 fn run_hook(key: &'static LocalKey<Hook>) {
244 if let Some(hook) = key.with(Cell::take) {
245 hook();
246 }
247 }
248
249 fn hook_waker() -> Waker {
250 static HOOK_VTABLE: RawWakerVTable = RawWakerVTable::new(
251 |p| {
252 run_hook(&ON_CLONE);
253 RawWaker::new(p, &HOOK_VTABLE)
254 },
255 |_| run_hook(&ON_WAKE),
256 |_| run_hook(&ON_WAKE),
257 |_| run_hook(&ON_DROP),
258 );
259 unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &HOOK_VTABLE)) }
260 }
261
262 #[derive(Default)]
263 struct WakeCounter(AtomicUsize);
264
265 impl Wake for WakeCounter {
266 fn wake(self: Arc<Self>) {
267 self.0.fetch_add(1, Ordering::Relaxed);
268 }
269 }
270
271 fn counted_waker() -> (Arc<WakeCounter>, Waker) {
272 let count = Arc::new(WakeCounter::default());
273 let waker = Waker::from(count.clone());
274 (count, waker)
275 }
276
277 #[test]
283 fn test_register_same_waker() {
284 static A: u8 = 0;
285 static B: u8 = 0;
286 let a = unsafe { Waker::from_raw(RawWaker::new((&raw const A).cast(), &VTABLE)) };
287 let b = unsafe { Waker::from_raw(RawWaker::new((&raw const B).cast(), &VTABLE)) };
288
289 let w = LocalWaker::new();
290 assert!(!w.register(&a));
291 assert!(w.register(&a));
292 assert!(w.register(&a));
293 assert_eq!(CLONES.load(Ordering::Relaxed), 1);
294
295 assert!(w.register(&b));
296 assert_eq!(CLONES.load(Ordering::Relaxed), 2);
297 assert!(w.take().unwrap().will_wake(&b));
298 }
299
300 #[test]
306 fn local_waker_clone_is_empty() {
307 let waker = LocalWaker::new();
308 waker.register(std::task::Waker::noop());
309 assert!(waker.is_set());
310 let cloned = waker.clone();
311 assert!(!cloned.is_set());
312 assert!(!cloned.wake_checked());
313 assert!(waker.is_set());
314 assert_eq!(format!("{waker:?}"), "LocalWaker");
315 }
316
317 #[test]
323 fn owning_conversions_and_wakes_do_not_clone() {
324 ON_CLONE.set(Some(Box::new(|| panic!("unexpected clone"))));
325 let calls = Rc::new(Cell::new(0));
326 for path in 0..4 {
327 let calls = calls.clone();
328 ON_WAKE.set(Some(Box::new(move || calls.set(calls.get() + 1))));
329 let slot = LocalWaker::with(Some(hook_waker()));
330 match path {
331 0 => slot.take().unwrap().wake(),
332 1 => slot.take_local().unwrap().wake(),
333 2 => slot.wake(),
334 _ => assert!(slot.wake_checked()),
335 }
336 assert!(slot.take().is_none());
337 assert!(slot.take_local().is_none());
338 assert!(!slot.wake_checked());
339 }
340 assert_eq!(calls.get(), 4);
341 assert!(ON_CLONE.with(Cell::take).is_some());
342 }
343
344 #[test]
350 fn clone_panic_preserves_registration() {
351 let (calls, original) = counted_waker();
352 let slot = LocalWaker::with(Some(original));
353 let replacement = hook_waker();
354 ON_CLONE.set(Some(Box::new(|| panic!("clone"))));
355 assert!(catch_unwind(AssertUnwindSafe(|| slot.register(&replacement))).is_err());
356 assert!(slot.wake_checked());
357 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
358 }
359
360 #[test]
366 fn clone_reentry_preserves_nested_changes_on_unwind() {
367 for panic in [false, true] {
368 let (previous_calls, previous) = counted_waker();
369 let (nested_calls, nested) = counted_waker();
370 let slot = Rc::new(LocalWaker::with(Some(previous)));
371 let callback_slot = slot.clone();
372 ON_CLONE.set(Some(Box::new(move || {
373 assert!(callback_slot.wake_checked());
374 assert!(!callback_slot.register(&nested));
375 assert!(!panic, "clone");
376 })));
377 let replacement = hook_waker();
378 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
379 if panic {
380 assert!(result.is_err());
381 } else {
382 assert!(result.unwrap());
383 }
384 assert_eq!(previous_calls.0.load(Ordering::Relaxed), 1);
385 assert!(slot.wake_checked());
386 assert_eq!(nested_calls.0.load(Ordering::Relaxed), usize::from(panic));
387 }
388 }
389
390 #[test]
396 fn replaced_drop_reentry_and_unwind() {
397 for panic in [false, true] {
398 let (replacement_calls, replacement) = counted_waker();
399 let (nested_calls, nested) = counted_waker();
400 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
401 let callback_slot = slot.clone();
402 ON_DROP.set(Some(Box::new(move || {
403 assert!(callback_slot.wake_checked());
404 assert!(!callback_slot.register(&nested));
405 assert!(!panic, "drop");
406 })));
407 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
408 if panic {
409 assert!(result.is_err());
410 } else {
411 assert!(result.unwrap());
412 }
413 assert_eq!(replacement_calls.0.load(Ordering::Relaxed), 1);
414 assert!(slot.wake_checked());
415 assert_eq!(nested_calls.0.load(Ordering::Relaxed), 1);
416 }
417 }
418
419 #[test]
425 fn wake_reentry_and_unwind() {
426 for panic in [false, true] {
427 for checked in [false, true] {
428 let (calls, nested) = counted_waker();
429 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
430 let callback_slot = slot.clone();
431 ON_WAKE.set(Some(Box::new(move || {
432 assert!(!callback_slot.is_set());
433 assert!(!callback_slot.register(&nested));
434 assert!(!panic, "wake");
435 })));
436 let result = catch_unwind(AssertUnwindSafe(|| {
437 if checked {
438 assert!(slot.wake_checked());
439 } else {
440 slot.wake();
441 }
442 }));
443 assert_eq!(result.is_err(), panic);
444 assert!(slot.wake_checked());
445 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
446 }
447 }
448 }
449
450 #[test]
456 fn native_slot_delivery_and_shutdown() {
457 #[derive(Debug)]
458 struct NoopNotify;
459
460 impl ntex_rt::Notify for NoopNotify {
461 fn notify(&self) -> std::io::Result<()> {
462 Ok(())
463 }
464 }
465
466 let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
467 let slot = Rc::new(LocalWaker::new());
468 let polls = Rc::new(Cell::new(0));
469 let (task_slot, task_polls) = (slot.clone(), polls.clone());
470 let task = rt.spawn(std::future::poll_fn(move |cx| {
471 task_polls.set(task_polls.get() + 1);
472 task_slot.register(cx.waker());
473 std::task::Poll::<()>::Pending
474 }));
475 rt.poll();
476 slot.take_local().unwrap().wake();
477 rt.poll();
478 let standard = slot.take().unwrap();
479 std::thread::spawn(move || standard.wake()).join().unwrap();
480 rt.poll();
481 assert_eq!(polls.get(), 3);
482 assert_eq!(Rc::strong_count(&slot), 2);
483 drop(rt);
484 assert!(task.is_finished());
485 assert_eq!(Rc::strong_count(&slot), 1);
486 assert!(slot.wake_checked());
487 assert!(!slot.wake_checked());
488 }
489}