1 | #![warn (rust_2018_idioms)] |
2 | #![cfg (all(feature = "full" , not(target_os = "wasi" )))] // Wasi doesn't support panic recovery |
3 | |
4 | use parking_lot::{const_mutex, Mutex}; |
5 | use std::error::Error; |
6 | use std::panic; |
7 | use std::sync::Arc; |
8 | use tokio::runtime::Runtime; |
9 | use tokio::sync::mpsc::channel; |
10 | use tokio::time::{Duration, Instant}; |
11 | use tokio_test::task; |
12 | use tokio_util::io::SyncIoBridge; |
13 | use tokio_util::sync::PollSender; |
14 | use tokio_util::task::LocalPoolHandle; |
15 | use tokio_util::time::DelayQueue; |
16 | |
17 | // Taken from tokio-util::time::wheel, if that changes then |
18 | const MAX_DURATION_MS: u64 = (1 << (36)) - 1; |
19 | |
20 | fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> { |
21 | static PANIC_MUTEX: Mutex<()> = const_mutex(()); |
22 | |
23 | { |
24 | let _guard = PANIC_MUTEX.lock(); |
25 | let panic_file: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None)); |
26 | |
27 | let prev_hook = panic::take_hook(); |
28 | { |
29 | let panic_file = panic_file.clone(); |
30 | panic::set_hook(Box::new(move |panic_info| { |
31 | let panic_location = panic_info.location().unwrap(); |
32 | panic_file |
33 | .lock() |
34 | .clone_from(&Some(panic_location.file().to_string())); |
35 | })); |
36 | } |
37 | |
38 | let result = panic::catch_unwind(func); |
39 | // Return to the previously set panic hook (maybe default) so that we get nice error |
40 | // messages in the tests. |
41 | panic::set_hook(prev_hook); |
42 | |
43 | if result.is_err() { |
44 | panic_file.lock().clone() |
45 | } else { |
46 | None |
47 | } |
48 | } |
49 | } |
50 | |
51 | #[test] |
52 | fn sync_bridge_new_panic_caller() -> Result<(), Box<dyn Error>> { |
53 | let panic_location_file = test_panic(|| { |
54 | let _ = SyncIoBridge::new(tokio::io::empty()); |
55 | }); |
56 | |
57 | // The panic location should be in this file |
58 | assert_eq!(&panic_location_file.unwrap(), file!()); |
59 | |
60 | Ok(()) |
61 | } |
62 | |
63 | #[test] |
64 | fn poll_sender_send_item_panic_caller() -> Result<(), Box<dyn Error>> { |
65 | let panic_location_file = test_panic(|| { |
66 | let (send, _) = channel::<u32>(3); |
67 | let mut send = PollSender::new(send); |
68 | |
69 | let _ = send.send_item(42); |
70 | }); |
71 | |
72 | // The panic location should be in this file |
73 | assert_eq!(&panic_location_file.unwrap(), file!()); |
74 | |
75 | Ok(()) |
76 | } |
77 | |
78 | #[test] |
79 | |
80 | fn local_pool_handle_new_panic_caller() -> Result<(), Box<dyn Error>> { |
81 | let panic_location_file = test_panic(|| { |
82 | let _ = LocalPoolHandle::new(0); |
83 | }); |
84 | |
85 | // The panic location should be in this file |
86 | assert_eq!(&panic_location_file.unwrap(), file!()); |
87 | |
88 | Ok(()) |
89 | } |
90 | |
91 | #[test] |
92 | |
93 | fn local_pool_handle_spawn_pinned_by_idx_panic_caller() -> Result<(), Box<dyn Error>> { |
94 | let panic_location_file = test_panic(|| { |
95 | let rt = basic(); |
96 | |
97 | rt.block_on(async { |
98 | let handle = LocalPoolHandle::new(2); |
99 | handle.spawn_pinned_by_idx(|| async { "test" }, 3); |
100 | }); |
101 | }); |
102 | |
103 | // The panic location should be in this file |
104 | assert_eq!(&panic_location_file.unwrap(), file!()); |
105 | |
106 | Ok(()) |
107 | } |
108 | #[test] |
109 | fn delay_queue_insert_at_panic_caller() -> Result<(), Box<dyn Error>> { |
110 | let panic_location_file = test_panic(|| { |
111 | let rt = basic(); |
112 | rt.block_on(async { |
113 | let mut queue = task::spawn(DelayQueue::with_capacity(3)); |
114 | |
115 | //let st = std::time::Instant::from(SystemTime::UNIX_EPOCH); |
116 | let _k = queue.insert_at( |
117 | "1" , |
118 | Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1), |
119 | ); |
120 | }); |
121 | }); |
122 | |
123 | // The panic location should be in this file |
124 | assert_eq!(&panic_location_file.unwrap(), file!()); |
125 | |
126 | Ok(()) |
127 | } |
128 | |
129 | #[test] |
130 | fn delay_queue_insert_panic_caller() -> Result<(), Box<dyn Error>> { |
131 | let panic_location_file = test_panic(|| { |
132 | let rt = basic(); |
133 | rt.block_on(async { |
134 | let mut queue = task::spawn(DelayQueue::with_capacity(3)); |
135 | |
136 | let _k = queue.insert("1" , Duration::from_millis(MAX_DURATION_MS + 1)); |
137 | }); |
138 | }); |
139 | |
140 | // The panic location should be in this file |
141 | assert_eq!(&panic_location_file.unwrap(), file!()); |
142 | |
143 | Ok(()) |
144 | } |
145 | |
146 | #[test] |
147 | fn delay_queue_remove_panic_caller() -> Result<(), Box<dyn Error>> { |
148 | let panic_location_file = test_panic(|| { |
149 | let rt = basic(); |
150 | rt.block_on(async { |
151 | let mut queue = task::spawn(DelayQueue::with_capacity(3)); |
152 | |
153 | let key = queue.insert_at("1" , Instant::now()); |
154 | queue.remove(&key); |
155 | queue.remove(&key); |
156 | }); |
157 | }); |
158 | |
159 | // The panic location should be in this file |
160 | assert_eq!(&panic_location_file.unwrap(), file!()); |
161 | |
162 | Ok(()) |
163 | } |
164 | |
165 | #[test] |
166 | fn delay_queue_reset_at_panic_caller() -> Result<(), Box<dyn Error>> { |
167 | let panic_location_file = test_panic(|| { |
168 | let rt = basic(); |
169 | rt.block_on(async { |
170 | let mut queue = task::spawn(DelayQueue::with_capacity(3)); |
171 | |
172 | let key = queue.insert_at("1" , Instant::now()); |
173 | queue.reset_at( |
174 | &key, |
175 | Instant::now() + Duration::from_millis(MAX_DURATION_MS + 1), |
176 | ); |
177 | }); |
178 | }); |
179 | |
180 | // The panic location should be in this file |
181 | assert_eq!(&panic_location_file.unwrap(), file!()); |
182 | |
183 | Ok(()) |
184 | } |
185 | |
186 | #[test] |
187 | fn delay_queue_reset_panic_caller() -> Result<(), Box<dyn Error>> { |
188 | let panic_location_file = test_panic(|| { |
189 | let rt = basic(); |
190 | rt.block_on(async { |
191 | let mut queue = task::spawn(DelayQueue::with_capacity(3)); |
192 | |
193 | let key = queue.insert_at("1" , Instant::now()); |
194 | queue.reset(&key, Duration::from_millis(MAX_DURATION_MS + 1)); |
195 | }); |
196 | }); |
197 | |
198 | // The panic location should be in this file |
199 | assert_eq!(&panic_location_file.unwrap(), file!()); |
200 | |
201 | Ok(()) |
202 | } |
203 | |
204 | #[test] |
205 | fn delay_queue_reserve_panic_caller() -> Result<(), Box<dyn Error>> { |
206 | let panic_location_file = test_panic(|| { |
207 | let rt = basic(); |
208 | rt.block_on(async { |
209 | let mut queue = task::spawn(DelayQueue::<u32>::with_capacity(3)); |
210 | |
211 | queue.reserve((1 << 30) as usize); |
212 | }); |
213 | }); |
214 | |
215 | // The panic location should be in this file |
216 | assert_eq!(&panic_location_file.unwrap(), file!()); |
217 | |
218 | Ok(()) |
219 | } |
220 | |
221 | fn basic() -> Runtime { |
222 | tokio::runtime::Builder::new_current_thread() |
223 | .enable_all() |
224 | .build() |
225 | .unwrap() |
226 | } |
227 | |