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