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]
172 async fn yield_test() {
173 yield_to().await;
174 }
175
176 #[ntex::test]
177 async fn local_slot_wakes_current_task() {
178 crate::spawn(async {
179 let slot = LocalWaker::new();
180 let mut polls = 0;
181 std::future::poll_fn(|cx| {
182 polls += 1;
183 if polls == 4 {
184 return std::task::Poll::Ready(());
185 }
186 assert!(!slot.register(cx.waker()));
187 match polls {
188 1 => slot.wake(),
189 2 => slot.take_local().unwrap().wake(),
190 _ => slot.take().unwrap().wake(),
191 }
192 std::task::Poll::Pending
193 })
194 .await;
195 assert!(!slot.is_set());
196 })
197 .await
198 .unwrap();
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use std::panic::{AssertUnwindSafe, catch_unwind};
205 use std::rc::Rc;
206 use std::sync::Arc;
207 use std::sync::atomic::{AtomicUsize, Ordering};
208 use std::task::{RawWaker, RawWakerVTable, Wake};
209 use std::thread::LocalKey;
210
211 use super::*;
212
213 static CLONES: AtomicUsize = AtomicUsize::new(0);
214
215 static VTABLE: RawWakerVTable = RawWakerVTable::new(
216 |p| {
217 CLONES.fetch_add(1, Ordering::Relaxed);
218 RawWaker::new(p, &VTABLE)
219 },
220 |_| {},
221 |_| {},
222 |_| {},
223 );
224
225 type Hook = Cell<Option<Box<dyn FnOnce()>>>;
226
227 thread_local! {
229 static ON_CLONE: Hook = Cell::new(None);
230 static ON_DROP: Hook = Cell::new(None);
231 static ON_WAKE: Hook = Cell::new(None);
232 }
233
234 fn run_hook(key: &'static LocalKey<Hook>) {
235 if let Some(hook) = key.with(Cell::take) {
236 hook();
237 }
238 }
239
240 fn hook_waker() -> Waker {
241 static HOOK_VTABLE: RawWakerVTable = RawWakerVTable::new(
242 |p| {
243 run_hook(&ON_CLONE);
244 RawWaker::new(p, &HOOK_VTABLE)
245 },
246 |_| run_hook(&ON_WAKE),
247 |_| run_hook(&ON_WAKE),
248 |_| run_hook(&ON_DROP),
249 );
250 unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &HOOK_VTABLE)) }
251 }
252
253 #[derive(Default)]
254 struct WakeCounter(AtomicUsize);
255
256 impl Wake for WakeCounter {
257 fn wake(self: Arc<Self>) {
258 self.0.fetch_add(1, Ordering::Relaxed);
259 }
260 }
261
262 fn counted_waker() -> (Arc<WakeCounter>, Waker) {
263 let count = Arc::new(WakeCounter::default());
264 let waker = Waker::from(count.clone());
265 (count, waker)
266 }
267
268 #[test]
269 fn test_register_same_waker() {
270 static A: u8 = 0;
271 static B: u8 = 0;
272 let a = unsafe { Waker::from_raw(RawWaker::new((&raw const A).cast(), &VTABLE)) };
273 let b = unsafe { Waker::from_raw(RawWaker::new((&raw const B).cast(), &VTABLE)) };
274
275 let w = LocalWaker::new();
276 assert!(!w.register(&a));
277 assert!(w.register(&a));
278 assert!(w.register(&a));
279 assert_eq!(CLONES.load(Ordering::Relaxed), 1);
280
281 assert!(w.register(&b));
282 assert_eq!(CLONES.load(Ordering::Relaxed), 2);
283 assert!(w.take().unwrap().will_wake(&b));
284 }
285
286 #[test]
287 fn local_waker_clone_is_empty() {
288 let waker = LocalWaker::new();
289 waker.register(std::task::Waker::noop());
290 assert!(waker.is_set());
291 let cloned = waker.clone();
292 assert!(!cloned.is_set());
293 assert!(!cloned.wake_checked());
294 assert!(waker.is_set());
295 assert_eq!(format!("{waker:?}"), "LocalWaker");
296 }
297
298 #[test]
299 fn owning_conversions_and_wakes_do_not_clone() {
300 ON_CLONE.set(Some(Box::new(|| panic!("unexpected clone"))));
301 let calls = Rc::new(Cell::new(0));
302 for path in 0..4 {
303 let calls = calls.clone();
304 ON_WAKE.set(Some(Box::new(move || calls.set(calls.get() + 1))));
305 let slot = LocalWaker::with(Some(hook_waker()));
306 match path {
307 0 => slot.take().unwrap().wake(),
308 1 => slot.take_local().unwrap().wake(),
309 2 => slot.wake(),
310 _ => assert!(slot.wake_checked()),
311 }
312 assert!(slot.take().is_none());
313 assert!(slot.take_local().is_none());
314 assert!(!slot.wake_checked());
315 }
316 assert_eq!(calls.get(), 4);
317 assert!(ON_CLONE.with(Cell::take).is_some());
318 }
319
320 #[test]
321 fn clone_panic_preserves_registration() {
322 let (calls, original) = counted_waker();
323 let slot = LocalWaker::with(Some(original));
324 let replacement = hook_waker();
325 ON_CLONE.set(Some(Box::new(|| panic!("clone"))));
326 assert!(catch_unwind(AssertUnwindSafe(|| slot.register(&replacement))).is_err());
327 assert!(slot.wake_checked());
328 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
329 }
330
331 #[test]
332 fn clone_reentry_preserves_nested_changes_on_unwind() {
333 for panic in [false, true] {
334 let (previous_calls, previous) = counted_waker();
335 let (nested_calls, nested) = counted_waker();
336 let slot = Rc::new(LocalWaker::with(Some(previous)));
337 let callback_slot = slot.clone();
338 ON_CLONE.set(Some(Box::new(move || {
339 assert!(callback_slot.wake_checked());
340 assert!(!callback_slot.register(&nested));
341 assert!(!panic, "clone");
342 })));
343 let replacement = hook_waker();
344 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
345 if panic {
346 assert!(result.is_err());
347 } else {
348 assert!(result.unwrap());
349 }
350 assert_eq!(previous_calls.0.load(Ordering::Relaxed), 1);
351 assert!(slot.wake_checked());
352 assert_eq!(nested_calls.0.load(Ordering::Relaxed), usize::from(panic));
353 }
354 }
355
356 #[test]
357 fn replaced_drop_reentry_and_unwind() {
358 for panic in [false, true] {
359 let (replacement_calls, replacement) = counted_waker();
360 let (nested_calls, nested) = counted_waker();
361 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
362 let callback_slot = slot.clone();
363 ON_DROP.set(Some(Box::new(move || {
364 assert!(callback_slot.wake_checked());
365 assert!(!callback_slot.register(&nested));
366 assert!(!panic, "drop");
367 })));
368 let result = catch_unwind(AssertUnwindSafe(|| slot.register(&replacement)));
369 if panic {
370 assert!(result.is_err());
371 } else {
372 assert!(result.unwrap());
373 }
374 assert_eq!(replacement_calls.0.load(Ordering::Relaxed), 1);
375 assert!(slot.wake_checked());
376 assert_eq!(nested_calls.0.load(Ordering::Relaxed), 1);
377 }
378 }
379
380 #[test]
381 fn wake_reentry_and_unwind() {
382 for panic in [false, true] {
383 for checked in [false, true] {
384 let (calls, nested) = counted_waker();
385 let slot = Rc::new(LocalWaker::with(Some(hook_waker())));
386 let callback_slot = slot.clone();
387 ON_WAKE.set(Some(Box::new(move || {
388 assert!(!callback_slot.is_set());
389 assert!(!callback_slot.register(&nested));
390 assert!(!panic, "wake");
391 })));
392 let result = catch_unwind(AssertUnwindSafe(|| {
393 if checked {
394 assert!(slot.wake_checked());
395 } else {
396 slot.wake();
397 }
398 }));
399 assert_eq!(result.is_err(), panic);
400 assert!(slot.wake_checked());
401 assert_eq!(calls.0.load(Ordering::Relaxed), 1);
402 }
403 }
404 }
405
406 #[test]
407 fn native_slot_delivery_and_shutdown() {
408 #[derive(Debug)]
409 struct NoopNotify;
410
411 impl ntex_rt::Notify for NoopNotify {
412 fn notify(&self) -> std::io::Result<()> {
413 Ok(())
414 }
415 }
416
417 let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
418 let slot = Rc::new(LocalWaker::new());
419 let polls = Rc::new(Cell::new(0));
420 let (task_slot, task_polls) = (slot.clone(), polls.clone());
421 let task = rt.spawn(std::future::poll_fn(move |cx| {
422 task_polls.set(task_polls.get() + 1);
423 task_slot.register(cx.waker());
424 std::task::Poll::<()>::Pending
425 }));
426 rt.poll();
427 slot.take_local().unwrap().wake();
428 rt.poll();
429 let standard = slot.take().unwrap();
430 std::thread::spawn(move || standard.wake()).join().unwrap();
431 rt.poll();
432 assert_eq!(polls.get(), 3);
433 assert_eq!(Rc::strong_count(&slot), 2);
434 drop(rt);
435 assert!(task.is_finished());
436 assert_eq!(Rc::strong_count(&slot), 1);
437 assert!(slot.wake_checked());
438 assert!(!slot.wake_checked());
439 }
440}