diff --git a/tokio/src/runtime/builder.rs b/tokio/src/runtime/builder.rs index 924aac98ef3..490bc96d5a9 100644 --- a/tokio/src/runtime/builder.rs +++ b/tokio/src/runtime/builder.rs @@ -287,7 +287,7 @@ impl Builder { /// /// The default value is the number of cores available to the system. /// - /// # Panic + /// # Panics /// /// When using the `current_thread` runtime this method will panic, since /// those variants do not allow setting worker thread counts. @@ -324,9 +324,10 @@ impl Builder { /// rt.block_on(async move {}); /// ``` /// - /// # Panic + /// # Panics /// /// This will panic if `val` is not larger than `0`. + #[track_caller] pub fn worker_threads(&mut self, val: usize) -> &mut Self { assert!(val > 0, "Worker threads cannot be set to 0"); self.worker_threads = Some(val); @@ -342,7 +343,7 @@ impl Builder { /// /// The default value is 512. /// - /// # Panic + /// # Panics /// /// This will panic if `val` is not larger than `0`. /// @@ -354,6 +355,7 @@ impl Builder { /// [`spawn_blocking`]: fn@crate::task::spawn_blocking /// [`worker_threads`]: Self::worker_threads /// [`thread_keep_alive`]: Self::thread_keep_alive + #[track_caller] #[cfg_attr(docsrs, doc(alias = "max_threads"))] pub fn max_blocking_threads(&mut self, val: usize) -> &mut Self { assert!(val > 0, "Max blocking threads cannot be set to 0"); diff --git a/tokio/src/runtime/context.rs b/tokio/src/runtime/context.rs index aebbe18755a..c31caf203ac 100644 --- a/tokio/src/runtime/context.rs +++ b/tokio/src/runtime/context.rs @@ -15,6 +15,7 @@ pub(crate) fn try_current() -> Result { } } +#[track_caller] pub(crate) fn current() -> Handle { match try_current() { Ok(handle) => handle, diff --git a/tokio/src/runtime/handle.rs b/tokio/src/runtime/handle.rs index 118d537d2f1..14101070cd3 100644 --- a/tokio/src/runtime/handle.rs +++ b/tokio/src/runtime/handle.rs @@ -87,7 +87,7 @@ impl Handle { /// Returns a `Handle` view over the currently running `Runtime`. /// - /// # Panic + /// # Panics /// /// This will panic if called outside the context of a Tokio runtime. That means that you must /// call this on one of the threads **being run by the runtime**, or from a thread with an active @@ -129,6 +129,7 @@ impl Handle { /// # }); /// # } /// ``` + #[track_caller] pub fn current() -> Self { context::current() } diff --git a/tokio/src/runtime/mod.rs b/tokio/src/runtime/mod.rs index 399e602d2bf..b9e7e2de958 100644 --- a/tokio/src/runtime/mod.rs +++ b/tokio/src/runtime/mod.rs @@ -472,7 +472,6 @@ cfg_rt! { /// ``` /// /// [handle]: fn@Handle::block_on - #[track_caller] pub fn block_on(&self, future: F) -> F::Output { #[cfg(all(tokio_unstable, feature = "tracing"))] let future = crate::util::trace::task(future, "block_on", None, task::Id::next().as_u64()); diff --git a/tokio/src/runtime/task/error.rs b/tokio/src/runtime/task/error.rs index 22b688aa221..7cf602abd33 100644 --- a/tokio/src/runtime/task/error.rs +++ b/tokio/src/runtime/task/error.rs @@ -82,6 +82,7 @@ impl JoinError { /// } /// } /// ``` + #[track_caller] pub fn into_panic(self) -> Box { self.try_into_panic() .expect("`JoinError` reason is not a panic.") diff --git a/tokio/tests/rt_panic.rs b/tokio/tests/rt_panic.rs new file mode 100644 index 00000000000..240c3d0c606 --- /dev/null +++ b/tokio/tests/rt_panic.rs @@ -0,0 +1,105 @@ +#![warn(rust_2018_idioms)] +#![cfg(feature = "full")] + +use futures::future; +use parking_lot::{const_mutex, Mutex}; +use std::error::Error; +use std::panic; +use std::sync::Arc; +use tokio::runtime::{Builder, Handle, Runtime}; + +fn test_panic(func: Func) -> Option { + static PANIC_MUTEX: Mutex<()> = const_mutex(()); + + { + let _guard = PANIC_MUTEX.lock(); + let panic_file: Arc>> = Arc::new(Mutex::new(None)); + + let prev_hook = panic::take_hook(); + { + let panic_file = panic_file.clone(); + panic::set_hook(Box::new(move |panic_info| { + let panic_location = panic_info.location().unwrap(); + panic_file + .lock() + .clone_from(&Some(panic_location.file().to_string())); + })); + } + + let result = panic::catch_unwind(func); + // Return to the previously set panic hook (maybe default) so that we get nice error + // messages in the tests. + panic::set_hook(prev_hook); + + if result.is_err() { + panic_file.lock().clone() + } else { + None + } + } +} + +#[test] +fn current_handle_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(|| { + let _ = Handle::current(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn into_panic_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(move || { + let rt = basic(); + rt.block_on(async { + let handle = tokio::spawn(future::pending::<()>()); + + handle.abort(); + + let err = handle.await.unwrap_err(); + assert!(!&err.is_panic()); + + let _ = err.into_panic(); + }); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn builder_worker_threads_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(|| { + let _ = Builder::new_multi_thread().worker_threads(0).build(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +#[test] +fn builder_max_blocking_threads_panic_caller() -> Result<(), Box> { + let panic_location_file = test_panic(|| { + let _ = Builder::new_multi_thread().max_blocking_threads(0).build(); + }); + + // The panic location should be in this file + assert_eq!(&panic_location_file.unwrap(), file!()); + + Ok(()) +} + +fn basic() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() +}