#![allow(unknown_lints, unexpected_cfgs)] #![warn(rust_2018_idioms)] #![cfg(feature = "full")] use std::error::Error; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use tokio::runtime::Runtime; use tokio::sync::oneshot; use tokio::task::{self, Id, LocalSet}; mod support { pub mod panic; } use support::panic::test_panic; #[tokio::test(flavor = "current_thread")] async fn task_id_spawn() { tokio::spawn(async { println!("task id: {}", task::id()) }) .await .unwrap(); } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "current_thread")] async fn task_id_spawn_blocking() { task::spawn_blocking(|| println!("task id: {}", task::id())) .await .unwrap(); } #[tokio::test(flavor = "current_thread")] async fn task_id_collision_current_thread() { let handle1 = tokio::spawn(async { task::id() }); let handle2 = tokio::spawn(async { task::id() }); let (id1, id2) = tokio::join!(handle1, handle2); assert_ne!(id1.unwrap(), id2.unwrap()); } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "multi_thread")] async fn task_id_collision_multi_thread() { let handle1 = tokio::spawn(async { task::id() }); let handle2 = tokio::spawn(async { task::id() }); let (id1, id2) = tokio::join!(handle1, handle2); assert_ne!(id1.unwrap(), id2.unwrap()); } #[tokio::test(flavor = "current_thread")] async fn task_ids_match_current_thread() { let (tx, rx) = oneshot::channel(); let handle = tokio::spawn(async { let id = rx.await.unwrap(); assert_eq!(id, task::id()); }); tx.send(handle.id()).unwrap(); handle.await.unwrap(); } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "multi_thread")] async fn task_ids_match_multi_thread() { let (tx, rx) = oneshot::channel(); let handle = tokio::spawn(async { let id = rx.await.unwrap(); assert_eq!(id, task::id()); }); tx.send(handle.id()).unwrap(); handle.await.unwrap(); } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "multi_thread")] async fn task_id_future_destructor_completion() { struct MyFuture { tx: Option>, } impl Future for MyFuture { type Output = (); fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { Poll::Ready(()) } } impl Drop for MyFuture { fn drop(&mut self) { let _ = self.tx.take().unwrap().send(task::id()); } } let (tx, rx) = oneshot::channel(); let handle = tokio::spawn(MyFuture { tx: Some(tx) }); let id = handle.id(); handle.await.unwrap(); assert_eq!(rx.await.unwrap(), id); } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "multi_thread")] async fn task_id_future_destructor_abort() { struct MyFuture { tx: Option>, } impl Future for MyFuture { type Output = (); fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { Poll::Pending } } impl Drop for MyFuture { fn drop(&mut self) { let _ = self.tx.take().unwrap().send(task::id()); } } let (tx, rx) = oneshot::channel(); let handle = tokio::spawn(MyFuture { tx: Some(tx) }); let id = handle.id(); handle.abort(); assert!(handle.await.unwrap_err().is_cancelled()); assert_eq!(rx.await.unwrap(), id); } #[tokio::test(flavor = "current_thread")] async fn task_id_output_destructor_handle_dropped_before_completion() { struct MyOutput { tx: Option>, } impl Drop for MyOutput { fn drop(&mut self) { let _ = self.tx.take().unwrap().send(task::id()); } } struct MyFuture { tx: Option>, } impl Future for MyFuture { type Output = MyOutput; fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { Poll::Ready(MyOutput { tx: self.tx.take() }) } } let (tx, mut rx) = oneshot::channel(); let handle = tokio::spawn(MyFuture { tx: Some(tx) }); let id = handle.id(); drop(handle); assert!(rx.try_recv().is_err()); assert_eq!(rx.await.unwrap(), id); } #[tokio::test(flavor = "current_thread")] async fn task_id_output_destructor_handle_dropped_after_completion() { struct MyOutput { tx: Option>, } impl Drop for MyOutput { fn drop(&mut self) { let _ = self.tx.take().unwrap().send(task::id()); } } struct MyFuture { tx_output: Option>, tx_future: Option>, } impl Future for MyFuture { type Output = MyOutput; fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { let _ = self.tx_future.take().unwrap().send(()); Poll::Ready(MyOutput { tx: self.tx_output.take(), }) } } let (tx_output, mut rx_output) = oneshot::channel(); let (tx_future, rx_future) = oneshot::channel(); let handle = tokio::spawn(MyFuture { tx_output: Some(tx_output), tx_future: Some(tx_future), }); let id = handle.id(); rx_future.await.unwrap(); assert!(rx_output.try_recv().is_err()); drop(handle); assert_eq!(rx_output.await.unwrap(), id); } #[test] fn task_try_id_outside_task() { assert_eq!(None, task::try_id()); } #[cfg(not(target_os = "wasi"))] #[test] fn task_try_id_inside_block_on() { let rt = Runtime::new().unwrap(); rt.block_on(async { assert_eq!(None, task::try_id()); }); } #[tokio::test(flavor = "current_thread")] async fn task_id_spawn_local() { LocalSet::new() .run_until(async { task::spawn_local(async { println!("task id: {}", task::id()) }) .await .unwrap(); }) .await } #[tokio::test(flavor = "current_thread")] async fn task_id_nested_spawn_local() { LocalSet::new() .run_until(async { task::spawn_local(async { let parent_id = task::id(); LocalSet::new() .run_until(async { task::spawn_local(async move { assert_ne!(parent_id, task::id()); }) .await .unwrap(); }) .await; assert_eq!(parent_id, task::id()); }) .await .unwrap(); }) .await; } #[cfg(not(target_os = "wasi"))] #[tokio::test(flavor = "multi_thread")] async fn task_id_block_in_place_block_on_spawn() { use tokio::runtime::Builder; task::spawn(async { let parent_id = task::id(); task::block_in_place(move || { let rt = Builder::new_current_thread().build().unwrap(); rt.block_on(rt.spawn(async move { assert_ne!(parent_id, task::id()); })) .unwrap(); }); assert_eq!(parent_id, task::id()); }) .await .unwrap(); } #[test] #[cfg_attr(not(panic = "unwind"), ignore)] fn task_id_outside_task_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let _ = task::id(); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) } #[test] #[cfg_attr(not(panic = "unwind"), ignore)] fn task_id_inside_block_on_panic_caller() -> Result<(), Box> { let panic_location_file = test_panic(|| { let rt = Runtime::new().unwrap(); rt.block_on(async { task::id(); }); }); // The panic location should be in this file assert_eq!(&panic_location_file.unwrap(), file!()); Ok(()) }