fuchsia_async/runtime/fuchsia/executor/atomic_future/
hooks.rs1use crate::instrument::Hooks;
6
7use super::{AtomicFutureHandle, Meta, VTable};
8use fuchsia_sync::Mutex;
9use std::collections::HashMap;
10use std::ptr::NonNull;
11use std::task::{Context, Poll};
12
13#[derive(Default)]
16pub struct HooksMap(Mutex<HashMap<usize, NonNull<()>>>);
17
18unsafe impl Send for HooksMap {}
19unsafe impl Sync for HooksMap {}
20
21struct HooksWrapper<H> {
22 orig_vtable: &'static VTable,
23 hooks: H,
24}
25
26impl<H: Hooks> HooksWrapper<H> {
27 const VTABLE: VTable = VTable {
32 drop: Self::drop,
33 drop_future: Self::drop_future,
34 poll: Self::poll,
35 get_result: Self::get_result,
36 drop_result: Self::drop_result,
37 };
38
39 unsafe fn wrapper<'a>(meta: NonNull<Meta>) -> &'a mut Self {
42 let id = meta.as_ptr() as usize;
43 unsafe {
44 meta.as_ref()
45 .scope()
46 .executor()
47 .hooks_map
48 .0
49 .lock()
50 .get(&id)
51 .unwrap()
52 .cast::<Self>()
53 .as_mut()
54 }
55 }
56
57 unsafe fn drop(mut meta: NonNull<Meta>) {
58 let meta_ref = unsafe { meta.as_mut() };
59 let id = meta.as_ptr() as usize;
61 let hooks = unsafe {
62 Box::from_raw(
63 meta_ref
64 .scope()
65 .executor()
66 .hooks_map
67 .0
68 .lock()
69 .remove(&id)
70 .unwrap()
71 .cast::<Self>()
72 .as_mut(),
73 )
74 };
75 meta_ref.vtable = hooks.orig_vtable;
78 unsafe { (hooks.orig_vtable.drop)(meta) };
79 }
80
81 unsafe fn poll(meta: NonNull<Meta>, cx: &mut Context<'_>) -> Poll<()> {
82 let wrapper = unsafe { Self::wrapper(meta) };
83 wrapper.hooks.task_poll_start();
84 let result = unsafe { (wrapper.orig_vtable.poll)(meta, cx) };
85 wrapper.hooks.task_poll_end();
86 if result.is_ready() {
87 wrapper.hooks.task_completed();
88 }
89 result
90 }
91
92 unsafe fn drop_future(meta: NonNull<Meta>) {
93 unsafe { (Self::wrapper(meta).orig_vtable.drop_future)(meta) };
94 }
95
96 unsafe fn get_result(meta: NonNull<Meta>) -> *const () {
97 unsafe { (Self::wrapper(meta).orig_vtable.get_result)(meta) }
98 }
99
100 unsafe fn drop_result(meta: NonNull<Meta>) {
101 unsafe { (Self::wrapper(meta).orig_vtable.drop_result)(meta) };
102 }
103}
104
105impl AtomicFutureHandle<'_> {
106 pub fn add_hooks<H: Hooks>(&mut self, hooks: H) {
108 let id = self.id();
109 let meta: &mut Meta = unsafe { self.0.as_mut() };
111 {
112 let mut hooks_map = meta.scope().executor().hooks_map.0.lock();
113 assert!(
116 hooks_map
117 .insert(id, unsafe {
118 NonNull::new_unchecked(Box::into_raw(Box::new(HooksWrapper {
119 orig_vtable: meta.vtable,
120 hooks,
121 })))
122 .cast::<()>()
123 })
124 .is_none()
125 );
126 }
127 meta.vtable = &HooksWrapper::<H>::VTABLE;
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::Hooks;
135 use crate::runtime::fuchsia::executor::scope::Spawnable;
136 use crate::{SpawnableFuture, TestExecutor, yield_now};
137 use std::sync::Arc;
138 use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
139
140 #[test]
141 fn test_hooks() {
142 let mut executor = TestExecutor::new();
143 let scope = executor.global_scope();
144 let mut future = SpawnableFuture::new(async {
145 yield_now().await;
146 })
147 .into_task(scope.clone());
148 #[derive(Default)]
149 struct MyHooks {
150 poll_start: AtomicU32,
151 poll_end: AtomicU32,
152 completed: AtomicBool,
153 }
154 impl Hooks for Arc<MyHooks> {
155 fn task_completed(&mut self) {
156 assert!(!self.completed.load(Ordering::Relaxed));
157 self.completed.store(true, Ordering::Relaxed);
158 }
159 fn task_poll_start(&mut self) {
160 self.poll_start.fetch_add(1, Ordering::Relaxed);
161 }
162 fn task_poll_end(&mut self) {
163 self.poll_end.fetch_add(1, Ordering::Relaxed);
164 }
165 }
166 let my_hooks = Arc::new(MyHooks::default());
167 future.add_hooks(my_hooks.clone());
168 scope.insert_task(future, false);
169 assert!(executor.run_until_stalled(&mut std::future::pending::<()>()).is_pending());
170 assert_eq!(my_hooks.poll_start.load(Ordering::Relaxed), 2);
171 assert_eq!(my_hooks.poll_end.load(Ordering::Relaxed), 2);
172 assert!(my_hooks.completed.load(Ordering::Relaxed));
173 }
174}