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