// Spawn `N` tasks that return their index (`i`). fn spawn_index_tasks(set: &mut JoinSet<usize>, n: usize, on: Option<&LocalSet>) { for i in0..n { let rc = std::rc::Rc::new(i); match on {
None => set.spawn_local(asyncmove { *rc }),
Some(local) => set.spawn_local_on(asyncmove { *rc }, local),
};
}
}
// Spawn `N` “pending” tasks that own a `oneshot::Sender`. // When the task is aborted the sender is dropped, which is observed // via the returned `Receiver`s. fn spawn_pending_tasks(
set: &mut JoinSet<()>,
receivers: &mut Vec<oneshot::Receiver<()>>,
n: usize,
on: Option<&LocalSet>,
) { for _ in0..n { let (tx, rx) = oneshot::channel::<()>();
receivers.push(rx);
let fut = asyncmove {
pending::<()>().await;
drop(tx);
};
match on {
None => set.spawn_local(fut),
Some(local) => set.spawn_local_on(fut, local),
};
}
}
// Await every task in a JoinSet and assert every task returns its own index. asyncfn drain_joinset_and_assert(mut set: JoinSet<usize>, n: usize) { letmut seen = vec![false; n]; whilelet Some(res) = set.join_next().await { let idx = res.expect("task panicked");
seen[idx] = true;
}
assert!(seen.into_iter().all(|b| b));
assert!(set.is_empty());
}
// Await every receiver and assert they all return `Err` because the // corresponding sender (inside an aborted task) was dropped. asyncfn await_receivers_and_assert(receivers: Vec<oneshot::Receiver<()>>) { for rx in receivers {
assert!(
rx.await.is_err(), "the task should have been aborted and the sender dropped"
);
}
}
#[tokio::test(start_paused = true)] asyncfn test_with_sleep() { letmut set = JoinSet::new();
for i in0..10 {
set.spawn(asyncmove { i });
assert_eq!(set.len(), 1 + i);
}
set.detach_all();
assert_eq!(set.len(), 0);
assert!(set.join_next().await.is_none());
for i in0..10 {
set.spawn(asyncmove {
tokio::time::sleep(Duration::from_secs(i as u64)).await;
i
});
assert_eq!(set.len(), 1 + i);
}
// This ensures that `join_next` works correctly when the coop budget is // exhausted. #[tokio::test(flavor = "current_thread")] asyncfn join_set_coop() { // Large enough to trigger coop. const TASK_NUM: u32 = 1000;
for _ in0..TASK_NUM {
set.spawn(async {
SEM.add_permits(1);
});
}
// Wait for all tasks to complete. // // Since this is a `current_thread` runtime, there's no race condition // between the last permit being added and the task completing. let _ = SEM.acquire_many(TASK_NUM).await.unwrap();
letmut seen = [false; 10]; whilelet Some(res) = set.join_next().await { let idx = res.unwrap();
seen[idx] = true;
}
for s in &seen {
assert!(s);
}
}
mod spawn_local { usesuper::*;
#[test] #[should_panic(
expected = "`spawn_local` called from outside of a `task::LocalSet` or `runtime::LocalRuntime`"
)] fn panic_outside_any_runtime() { letmut set = JoinSet::new();
set.spawn_local(async {});
}
#[tokio::test(flavor = "multi_thread")] #[should_panic(
expected = "`spawn_local` called from outside of a `task::LocalSet` or `runtime::LocalRuntime`"
)] asyncfn panic_in_multi_thread_runtime() { letmut set = JoinSet::new();
set.spawn_local(async {});
}
#[cfg(tokio_unstable)] mod local_runtime { usesuper::*;
/// Spawn several tasks, and then join all tasks. #[tokio::test(flavor = "local")] asyncfn spawn_then_join_next() { const N: usize = 8;
letmut set = JoinSet::new();
spawn_index_tasks(&mut set, N, None);
/// Spawn several pending-forever tasks, and then drop the [`JoinSet`]. #[tokio::test(flavor = "local")] asyncfn spawn_then_drop() { const N: usize = 8; letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, None);
/// Spawn several tasks, and then join all tasks. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_join_next() { const N: usize = 8; let local = LocalSet::new();
local
.run_until(asyncmove { letmut set = JoinSet::new();
spawn_index_tasks(&mut set, N, None);
drain_joinset_and_assert(set, N).await;
})
.await;
}
/// Spawn several pending-forever tasks, and then shutdown the [`JoinSet`]. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_shutdown() { const N: usize = 8; let local = LocalSet::new();
local
.run_until(async { letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, None);
assert!(set.try_join_next().is_none());
/// Spawn several pending-forever tasks, and then drop the [`JoinSet`]. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_drop() { const N: usize = 8; let local = LocalSet::new();
local
.run_until(async { letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, None);
assert!(set.try_join_next().is_none());
#[cfg(tokio_unstable)] mod local_runtime { usesuper::*;
/// Spawn several tasks, and then join all tasks. #[tokio::test(flavor = "local")] asyncfn spawn_then_join_next() { const N: usize = 8;
let local = LocalSet::new(); letmut set = JoinSet::new();
spawn_index_tasks(&mut set, N, Some(&local));
assert!(set.try_join_next().is_none());
local
.run_until(asyncmove {
drain_joinset_and_assert(set, N).await;
})
.await;
}
}
mod local_set { usesuper::*;
/// Spawn several tasks, and then join all tasks. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_join_next() { const N: usize = 8; let local = LocalSet::new(); letmut pending_set = JoinSet::new();
spawn_index_tasks(&mut pending_set, N, Some(&local));
assert!(pending_set.try_join_next().is_none());
local
.run_until(asyncmove {
drain_joinset_and_assert(pending_set, N).await;
})
.await;
}
/// Spawn several pending-forever tasks, and then shutdown the [`JoinSet`]. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_shutdown() { const N: usize = 8; let local = LocalSet::new(); letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, Some(&local));
assert!(set.try_join_next().is_none());
local
.run_until(asyncmove {
set.shutdown().await;
assert!(set.is_empty());
await_receivers_and_assert(receivers).await;
})
.await;
}
/// Spawn several pending-forever tasks and then drop the [`JoinSet`] /// before the `LocalSet` is driven and while the `LocalSet` is already driven. #[tokio::test(flavor = "current_thread")] asyncfn spawn_then_drop() { const N: usize = 8;
{ let local = LocalSet::new(); letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, Some(&local));
assert!(set.try_join_next().is_none());
drop(set);
local
.run_until(asyncmove {
await_receivers_and_assert(receivers).await;
})
.await;
}
{ let local = LocalSet::new(); letmut set = JoinSet::new(); letmut receivers = Vec::new();
spawn_pending_tasks(&mut set, &mut receivers, N, Some(&local));
assert!(set.try_join_next().is_none());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.