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 pub(crate) fn register_in<'a>(slot: impl Fn() -> &'a LocalWaker, waker: &Waker) -> bool {
82 let prev = slot().waker.take();
83 let registered = prev.is_some();
84 let same = prev.as_ref().is_some_and(|prev| prev.will_wake(waker));
85 slot().waker.set(prev);
86 if !same {
87 let next = ntex_rt::LocalWaker::from_waker(waker);
88 let prev = slot().waker.replace(Some(next));
89 drop(prev);
90 }
91 registered
92 }
93
94 #[inline]
95 pub fn wake(&self) {
100 if let Some(waker) = self.take_local() {
101 waker.wake();
102 }
103 }
104
105 #[inline]
106 pub fn wake_checked(&self) -> bool {
111 if let Some(waker) = self.take_local() {
112 waker.wake();
113 true
114 } else {
115 false
116 }
117 }
118
119 pub fn take(&self) -> Option<Waker> {
125 self.take_local().map(ntex_rt::LocalWaker::into_waker)
126 }
127
128 #[inline]
133 pub fn take_local(&self) -> Option<ntex_rt::LocalWaker> {
134 self.waker.take()
135 }
136
137 #[doc(hidden)]
138 pub fn is_set(&self) -> bool {
140 let waker = self.waker.take();
141 let set = waker.is_some();
142 self.waker.set(waker);
143 set
144 }
145}
146
147impl Clone for LocalWaker {
149 fn clone(&self) -> Self {
150 LocalWaker::new()
151 }
152}
153
154impl fmt::Debug for LocalWaker {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 write!(f, "LocalWaker")
157 }
158}
159
160pub async fn yield_to() {
162 use std::{future::Future, pin::Pin, task::Context, task::Poll};
163
164 struct Yield {
165 completed: bool,
166 }
167
168 impl Future for Yield {
169 type Output = ();
170
171 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
172 if self.completed {
173 return Poll::Ready(());
174 }
175
176 self.completed = true;
177 cx.waker().wake_by_ref();
178
179 Poll::Pending
180 }
181 }
182
183 Yield { completed: false }.await;
184}
185
186#[cfg(test)]
187mod test {
188 use super::*;
189
190 #[ntex::test]
195 async fn yield_test() {
196 yield_to().await;
197 }
198
199 #[ntex::test]
205 async fn local_slot_wakes_current_task() {
206 crate::spawn(async {
207 let slot = LocalWaker::new();
208 let mut polls = 0;
209 std::future::poll_fn(|cx| {
210 polls += 1;
211 if polls == 4 {
212 return std::task::Poll::Ready(());
213 }
214 assert!(!slot.register(cx.waker()));
215 match polls {
216 1 => slot.wake(),
217 2 => slot.take_local().unwrap().wake(),
218 _ => slot.take().unwrap().wake(),
219 }
220 std::task::Poll::Pending
221 })
222 .await;
223 assert!(!slot.is_set());
224 })
225 .await
226 .unwrap();
227 }
228}
229
230#[cfg(test)]
232pub(crate) mod hooks {
233 use std::cell::Cell;
234 use std::task::{RawWaker, RawWakerVTable, Waker};
235 use std::thread::LocalKey;
236
237 pub(crate) type Hook = Cell<Option<Box<dyn FnOnce()>>>;
238
239 thread_local! {
241 pub(crate) static ON_CLONE: Hook = Cell::new(None);
242 pub(crate) static ON_DROP: Hook = Cell::new(None);
243 pub(crate) static ON_WAKE: Hook = Cell::new(None);
244 }
245
246 fn run_hook(key: &'static LocalKey<Hook>) {
247 if let Some(hook) = key.with(Cell::take) {
248 hook();
249 }
250 }
251
252 pub(crate) fn hook_waker() -> Waker {
254 static HOOK_VTABLE: RawWakerVTable = RawWakerVTable::new(
255 |p| {
256 run_hook(&ON_CLONE);
257 RawWaker::new(p, &HOOK_VTABLE)
258 },
259 |_| run_hook(&ON_WAKE),
260 |_| run_hook(&ON_WAKE),
261 |_| run_hook(&ON_DROP),
262 );
263 unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &HOOK_VTABLE)) }
264 }
265
266 #[derive(Default)]
268 pub(crate) struct WakeCount(pub(crate) std::sync::atomic::AtomicUsize);
269
270 impl std::task::Wake for WakeCount {
271 fn wake(self: std::sync::Arc<Self>) {
272 self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
273 }
274 }
275
276 impl WakeCount {
277 pub(crate) fn get(&self) -> usize {
278 self.0.load(std::sync::atomic::Ordering::Relaxed)
279 }
280 }
281
282 pub(crate) fn counted_waker() -> (std::sync::Arc<WakeCount>, Waker) {
284 let count = std::sync::Arc::new(WakeCount::default());
285 (count.clone(), Waker::from(count))
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use std::panic::{AssertUnwindSafe, catch_unwind};
292 use std::rc::Rc;
293 use std::sync::Arc;
294 use std::sync::atomic::{AtomicUsize, Ordering};
295 use std::task::{RawWaker, RawWakerVTable, Wake};
296
297 use super::hooks::{ON_CLONE, ON_DROP, ON_WAKE, hook_waker};
298 use super::*;
299
300 static CLONES: AtomicUsize = AtomicUsize::new(0);
301
302 static VTABLE: RawWakerVTable = RawWakerVTable::new(
303 |p| {
304 CLONES.fetch_add(1, Ordering::Relaxed);
305 RawWaker::new(p, &VTABLE)
306 },
307 |_| {},
308 |_| {},
309 |_| {},
310 );
311
312 #[derive(Default)]
313 struct WakeCounter(AtomicUsize);
314
315 impl Wake for WakeCounter {
316 fn wake(self: Arc<Self>) {
317 self.0.fetch_add(1, Ordering::Relaxed);
318 }
319 }
320
321 fn counted_waker() -> (Arc<WakeCounter>, Waker) {
322 let count = Arc::new(WakeCounter::default());
323 let waker = Waker::from(count.clone());
324 (count, waker)
325 }
326
327 #[test]
333 fn test_register_same_waker() {
334 static A: u8 = 0;
335 static B: u8 = 0;
336 let a = unsafe { Waker::from_raw(RawWaker::new((&raw const A).cast(), &VTABLE)) };
337 let b = unsafe { Waker::from_raw(RawWaker::new((&raw const B).cast(), &VTABLE)) };
338
339 let w = LocalWaker::new();
340 assert!(!w.register(&a));
341 assert!(w.register(&a));
342 assert!(w.register(&a));
343 assert_eq!(CLONES.load(Ordering::Relaxed), 1);
344
345 assert!(w.register(&b));
346 assert_eq!(CLONES.load(Ordering::Relaxed), 2);
347 assert!(w.take().unwrap().will_wake(&b));
348 }
349
350 #[test]
356 fn local_waker_clone_is_empty() {
357 let waker = LocalWaker::new();
358 waker.register(std::task::Waker::noop());
359 assert!(waker.is_set());
360 let cloned = waker.clone();
361 assert!(!cloned.is_set());
362 assert!(!cloned.wake_checked());
363 assert!(waker.is_set());
364 assert_eq!(format!("{waker:?}"), "LocalWaker");
365 }
366
367 #[test]
373 fn owning_conversions_and_wakes_do_not_clone() {
374 ON_CLONE.set(Some(Box::new(|| panic!("unexpected clone"))));
375 let calls = Rc::new(Cell::new(0));
376 for path in 0..4 {
377 let calls = calls.clone();
378 ON_WAKE.set(Some(Box::new(move || calls.set(calls.get() + 1))));
379 let slot = LocalWaker::with(Some(hook_waker()));
380 match path {
381 0 => slot.take().unwrap().wake(),
382 1 => slot.take_local().unwrap().wake(),
383 2 => slot.wake(),
384 _ => assert!(slot.wake_checked()),
385 }
386 assert!(slot.take().is_none());
387 assert!(slot.take_local().is_none());
388 assert!(!slot.wake_checked());
389 }
390 assert_eq!(calls.get(), 4);
391 assert!(ON_CLONE.with(Cell::take).is_some());
392 }
393
394 #[test]
400 fn clone_panic_preserves_registration() {
401 let (calls, original) = counted_waker();
402 let slot = LocalWaker::with(Some(original));
403 let replacement = hook_waker();
404 ON_CLONE.set(Some(Box::new(|| panic!("clone"))));
405 assert!(catch_unwind(AssertUnwindSafe(|| slot.register(&replacement))).is_err());
406 assert!(slot.wake_checked());
407 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
408 }
409
410 #[test]
416 fn clone_reentry_preserves_nested_changes_on_unwind() {
417 for panic in [false, true] {
418 let (previous_calls, previous) = counted_waker();
419 let (nested_calls, nested) = counted_waker();
420 let slot = Rc::new(LocalWaker::with(Some(previous)));
421 let callback_slot = slot.clone();
422 ON_CLONE.set(Some(Box::new(move || {
423 assert!(callback_slot.wake_checked());
424 assert!(!callback_slot.register(&nested));
425 assert!(!panic, "clone");
426 })));
427 let replacement = hook_waker();
428 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
429 if panic {
430 assert!(result.is_err());
431 } else {
432 assert!(result.unwrap());
433 }
434 assert_eq!(previous_calls.0.load(Ordering::Relaxed), 1);
435 assert!(slot.wake_checked());
436 assert_eq!(nested_calls.0.load(Ordering::Relaxed), usize::from(panic));
437 }
438 }
439
440 #[test]
446 fn replaced_drop_reentry_and_unwind() {
447 for panic in [false, true] {
448 let (replacement_calls, replacement) = counted_waker();
449 let (nested_calls, nested) = counted_waker();
450 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
451 let callback_slot = slot.clone();
452 ON_DROP.set(Some(Box::new(move || {
453 assert!(callback_slot.wake_checked());
454 assert!(!callback_slot.register(&nested));
455 assert!(!panic, "drop");
456 })));
457 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
458 if panic {
459 assert!(result.is_err());
460 } else {
461 assert!(result.unwrap());
462 }
463 assert_eq!(replacement_calls.0.load(Ordering::Relaxed), 1);
464 assert!(slot.wake_checked());
465 assert_eq!(nested_calls.0.load(Ordering::Relaxed), 1);
466 }
467 }
468
469 #[test]
475 fn wake_reentry_and_unwind() {
476 for panic in [false, true] {
477 for checked in [false, true] {
478 let (calls, nested) = counted_waker();
479 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
480 let callback_slot = slot.clone();
481 ON_WAKE.set(Some(Box::new(move || {
482 assert!(!callback_slot.is_set());
483 assert!(!callback_slot.register(&nested));
484 assert!(!panic, "wake");
485 })));
486 let result = catch_unwind(AssertUnwindSafe(|| {
487 if checked {
488 assert!(slot.wake_checked());
489 } else {
490 slot.wake();
491 }
492 }));
493 assert_eq!(result.is_err(), panic);
494 assert!(slot.wake_checked());
495 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
496 }
497 }
498 }
499
500 #[test]
506 fn native_slot_delivery_and_shutdown() {
507 #[derive(Debug)]
508 struct NoopNotify;
509
510 impl ntex_rt::Notify for NoopNotify {
511 fn notify(&self) -> std::io::Result<()> {
512 Ok(())
513 }
514 }
515
516 let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
517 let slot = Rc::new(LocalWaker::new());
518 let polls = Rc::new(Cell::new(0));
519 let (task_slot, task_polls) = (slot.clone(), polls.clone());
520 let task = rt.spawn(std::future::poll_fn(move |cx| {
521 task_polls.set(task_polls.get() + 1);
522 task_slot.register(cx.waker());
523 std::task::Poll::<()>::Pending
524 }));
525 rt.poll();
526 slot.take_local().unwrap().wake();
527 rt.poll();
528 let standard = slot.take().unwrap();
529 std::thread::spawn(move || standard.wake()).join().unwrap();
530 rt.poll();
531 assert_eq!(polls.get(), 3);
532 assert_eq!(Rc::strong_count(&slot), 2);
533 drop(rt);
534 assert!(task.is_finished());
535 assert_eq!(Rc::strong_count(&slot), 1);
536 assert!(slot.wake_checked());
537 assert!(!slot.wake_checked());
538 }
539}