mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
ci: test no const mutex new (#5257)
This adds CI coverage for a couple of code paths that are not currently hit in CI: * no `const fn Mutex::new` * no `AtomicU64` This is done by adding some new CFG flags used only for tests in order to force those code paths.
This commit is contained in:
@@ -268,26 +268,42 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
target:
|
include:
|
||||||
- i686-unknown-linux-gnu
|
- target: i686-unknown-linux-gnu
|
||||||
- arm-unknown-linux-gnueabihf
|
- target: arm-unknown-linux-gnueabihf
|
||||||
- armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
- aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
# Run a platform without AtomicU64 and no const Mutex::new
|
||||||
|
- target: arm-unknown-linux-gnueabihf
|
||||||
|
rustflags: --cfg tokio_no_const_mutex_new
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install Rust ${{ env.rust_stable }}
|
- name: Install Rust stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.rust_stable }}
|
toolchain: ${{ env.rust_stable }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
|
# First run with all features (including parking_lot)
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
use-cross: true
|
use-cross: true
|
||||||
command: test
|
command: test
|
||||||
args: -p tokio --all-features --target ${{ matrix.target }} --tests
|
args: -p tokio --all-features --target ${{ matrix.target }} --tests
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6
|
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 ${{ matrix.rustflags }}
|
||||||
|
# Now run without parking_lot
|
||||||
|
- name: Remove `parking_lot` from `full` feature
|
||||||
|
run: sed -i '0,/parking_lot/{/parking_lot/d;}' tokio/Cargo.toml
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: test
|
||||||
|
# The `tokio_no_parking_lot` cfg is here to ensure the `sed` above does not silently break.
|
||||||
|
args: -p tokio --features full,test-util --target ${{ matrix.target }} --tests
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6 --cfg tokio_no_parking_lot ${{ matrix.rustflags }}
|
||||||
|
|
||||||
# See https://github.com/tokio-rs/tokio/issues/5187
|
# See https://github.com/tokio-rs/tokio/issues/5187
|
||||||
no-atomic-u64:
|
no-atomic-u64:
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
#![cfg(not(any(feature = "full", tokio_wasm)))]
|
#[cfg(not(any(feature = "full", tokio_wasm)))]
|
||||||
compile_error!("run main Tokio tests with `--features full`");
|
compile_error!("run main Tokio tests with `--features full`");
|
||||||
|
|
||||||
|
// CI sets `--cfg tokio_no_parking_lot` when trying to run tests with
|
||||||
|
// `parking_lot` disabled. This check prevents "silent failure" if `parking_lot`
|
||||||
|
// accidentally gets enabled.
|
||||||
|
#[cfg(all(tokio_no_parking_lot, feature = "parking_lot"))]
|
||||||
|
compile_error!("parking_lot feature enabled when it should not be");
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use parking_lot::{const_mutex, Mutex};
|
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
pub fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> {
|
pub fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<String> {
|
||||||
static PANIC_MUTEX: Mutex<()> = const_mutex(());
|
static PANIC_MUTEX: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
{
|
{
|
||||||
let _guard = PANIC_MUTEX.lock();
|
let _guard = PANIC_MUTEX.lock();
|
||||||
@@ -16,6 +15,7 @@ pub fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<Stri
|
|||||||
let panic_location = panic_info.location().unwrap();
|
let panic_location = panic_info.location().unwrap();
|
||||||
panic_file
|
panic_file
|
||||||
.lock()
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.clone_from(&Some(panic_location.file().to_string()));
|
.clone_from(&Some(panic_location.file().to_string()));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ pub fn test_panic<Func: FnOnce() + panic::UnwindSafe>(func: Func) -> Option<Stri
|
|||||||
panic::set_hook(prev_hook);
|
panic::set_hook(prev_hook);
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
panic_file.lock().clone()
|
panic_file.lock().unwrap().clone()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
+183
-172
@@ -4,178 +4,7 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Drop;
|
use std::ops::Drop;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use std::time::Duration;
|
use tokio::sync::OnceCell;
|
||||||
use tokio::runtime;
|
|
||||||
use tokio::sync::{OnceCell, SetError};
|
|
||||||
use tokio::time;
|
|
||||||
|
|
||||||
async fn func1() -> u32 {
|
|
||||||
5
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn func2() -> u32 {
|
|
||||||
time::sleep(Duration::from_millis(1)).await;
|
|
||||||
10
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn func_err() -> Result<u32, ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn func_ok() -> Result<u32, ()> {
|
|
||||||
Ok(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn func_panic() -> u32 {
|
|
||||||
time::sleep(Duration::from_millis(1)).await;
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn sleep_and_set() -> u32 {
|
|
||||||
// Simulate sleep by pausing time and waiting for another thread to
|
|
||||||
// resume clock when calling `set`, then finding the cell being initialized
|
|
||||||
// by this call
|
|
||||||
time::sleep(Duration::from_millis(2)).await;
|
|
||||||
5
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn advance_time_and_set(cell: &'static OnceCell<u32>, v: u32) -> Result<(), SetError<u32>> {
|
|
||||||
time::advance(Duration::from_millis(1)).await;
|
|
||||||
cell.set(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_or_init() {
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_time()
|
|
||||||
.start_paused(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await });
|
|
||||||
let handle2 = rt.spawn(async { ONCE.get_or_init(func2).await });
|
|
||||||
|
|
||||||
time::advance(Duration::from_millis(1)).await;
|
|
||||||
time::resume();
|
|
||||||
|
|
||||||
let result1 = handle1.await.unwrap();
|
|
||||||
let result2 = handle2.await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(*result1, 5);
|
|
||||||
assert_eq!(*result2, 5);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_or_init_panic() {
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_time()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
time::pause();
|
|
||||||
|
|
||||||
let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await });
|
|
||||||
let handle2 = rt.spawn(async { ONCE.get_or_init(func_panic).await });
|
|
||||||
|
|
||||||
time::advance(Duration::from_millis(1)).await;
|
|
||||||
|
|
||||||
let result1 = handle1.await.unwrap();
|
|
||||||
let result2 = handle2.await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(*result1, 5);
|
|
||||||
assert_eq!(*result2, 5);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_and_get() {
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_time()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let _ = rt.spawn(async { ONCE.set(5) }).await;
|
|
||||||
let value = ONCE.get().unwrap();
|
|
||||||
assert_eq!(*value, 5);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_uninit() {
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
let uninit = ONCE.get();
|
|
||||||
assert!(uninit.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_twice() {
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
let first = ONCE.set(5);
|
|
||||||
assert_eq!(first, Ok(()));
|
|
||||||
let second = ONCE.set(6);
|
|
||||||
assert!(second.err().unwrap().is_already_init_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_while_initializing() {
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_time()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
time::pause();
|
|
||||||
|
|
||||||
let handle1 = rt.spawn(async { ONCE.get_or_init(sleep_and_set).await });
|
|
||||||
let handle2 = rt.spawn(async { advance_time_and_set(&ONCE, 10).await });
|
|
||||||
|
|
||||||
time::advance(Duration::from_millis(2)).await;
|
|
||||||
|
|
||||||
let result1 = handle1.await.unwrap();
|
|
||||||
let result2 = handle2.await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(*result1, 5);
|
|
||||||
assert!(result2.err().unwrap().is_initializing_err());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_or_try_init() {
|
|
||||||
let rt = runtime::Builder::new_current_thread()
|
|
||||||
.enable_time()
|
|
||||||
.start_paused(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
|
||||||
|
|
||||||
rt.block_on(async {
|
|
||||||
let handle1 = rt.spawn(async { ONCE.get_or_try_init(func_err).await });
|
|
||||||
let handle2 = rt.spawn(async { ONCE.get_or_try_init(func_ok).await });
|
|
||||||
|
|
||||||
time::advance(Duration::from_millis(1)).await;
|
|
||||||
time::resume();
|
|
||||||
|
|
||||||
let result1 = handle1.await.unwrap();
|
|
||||||
assert!(result1.is_err());
|
|
||||||
|
|
||||||
let result2 = handle2.await.unwrap();
|
|
||||||
assert_eq!(*result2.unwrap(), 10);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn drop_cell() {
|
fn drop_cell() {
|
||||||
@@ -272,3 +101,185 @@ fn from() {
|
|||||||
let cell = OnceCell::from(2);
|
let cell = OnceCell::from(2);
|
||||||
assert_eq!(*cell.get().unwrap(), 2);
|
assert_eq!(*cell.get().unwrap(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parking_lot")]
|
||||||
|
mod parking_lot {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use tokio::runtime;
|
||||||
|
use tokio::sync::SetError;
|
||||||
|
use tokio::time;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
async fn func1() -> u32 {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func2() -> u32 {
|
||||||
|
time::sleep(Duration::from_millis(1)).await;
|
||||||
|
10
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func_err() -> Result<u32, ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func_ok() -> Result<u32, ()> {
|
||||||
|
Ok(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn func_panic() -> u32 {
|
||||||
|
time::sleep(Duration::from_millis(1)).await;
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sleep_and_set() -> u32 {
|
||||||
|
// Simulate sleep by pausing time and waiting for another thread to
|
||||||
|
// resume clock when calling `set`, then finding the cell being initialized
|
||||||
|
// by this call
|
||||||
|
time::sleep(Duration::from_millis(2)).await;
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn advance_time_and_set(
|
||||||
|
cell: &'static OnceCell<u32>,
|
||||||
|
v: u32,
|
||||||
|
) -> Result<(), SetError<u32>> {
|
||||||
|
time::advance(Duration::from_millis(1)).await;
|
||||||
|
cell.set(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_or_init() {
|
||||||
|
let rt = runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.start_paused(true)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await });
|
||||||
|
let handle2 = rt.spawn(async { ONCE.get_or_init(func2).await });
|
||||||
|
|
||||||
|
time::advance(Duration::from_millis(1)).await;
|
||||||
|
time::resume();
|
||||||
|
|
||||||
|
let result1 = handle1.await.unwrap();
|
||||||
|
let result2 = handle2.await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*result1, 5);
|
||||||
|
assert_eq!(*result2, 5);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_or_init_panic() {
|
||||||
|
let rt = runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
time::pause();
|
||||||
|
|
||||||
|
let handle1 = rt.spawn(async { ONCE.get_or_init(func1).await });
|
||||||
|
let handle2 = rt.spawn(async { ONCE.get_or_init(func_panic).await });
|
||||||
|
|
||||||
|
time::advance(Duration::from_millis(1)).await;
|
||||||
|
|
||||||
|
let result1 = handle1.await.unwrap();
|
||||||
|
let result2 = handle2.await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*result1, 5);
|
||||||
|
assert_eq!(*result2, 5);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_and_get() {
|
||||||
|
let rt = runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
let _ = rt.spawn(async { ONCE.set(5) }).await;
|
||||||
|
let value = ONCE.get().unwrap();
|
||||||
|
assert_eq!(*value, 5);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_uninit() {
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
let uninit = ONCE.get();
|
||||||
|
assert!(uninit.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_twice() {
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
let first = ONCE.set(5);
|
||||||
|
assert_eq!(first, Ok(()));
|
||||||
|
let second = ONCE.set(6);
|
||||||
|
assert!(second.err().unwrap().is_already_init_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_while_initializing() {
|
||||||
|
let rt = runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
time::pause();
|
||||||
|
|
||||||
|
let handle1 = rt.spawn(async { ONCE.get_or_init(sleep_and_set).await });
|
||||||
|
let handle2 = rt.spawn(async { advance_time_and_set(&ONCE, 10).await });
|
||||||
|
|
||||||
|
time::advance(Duration::from_millis(2)).await;
|
||||||
|
|
||||||
|
let result1 = handle1.await.unwrap();
|
||||||
|
let result2 = handle2.await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(*result1, 5);
|
||||||
|
assert!(result2.err().unwrap().is_initializing_err());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_or_try_init() {
|
||||||
|
let rt = runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.start_paused(true)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
static ONCE: OnceCell<u32> = OnceCell::const_new();
|
||||||
|
|
||||||
|
rt.block_on(async {
|
||||||
|
let handle1 = rt.spawn(async { ONCE.get_or_try_init(func_err).await });
|
||||||
|
let handle2 = rt.spawn(async { ONCE.get_or_try_init(func_ok).await });
|
||||||
|
|
||||||
|
time::advance(Duration::from_millis(1)).await;
|
||||||
|
time::resume();
|
||||||
|
|
||||||
|
let result1 = handle1.await.unwrap();
|
||||||
|
assert!(result1.is_err());
|
||||||
|
|
||||||
|
let result2 = handle2.await.unwrap();
|
||||||
|
assert_eq!(*result2.unwrap(), 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ use tokio::sync::oneshot;
|
|||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
use tokio::time::Duration;
|
use tokio::time::Duration;
|
||||||
|
|
||||||
use futures::future::FutureExt;
|
|
||||||
|
|
||||||
fn rt() -> tokio::runtime::Runtime {
|
fn rt() -> tokio::runtime::Runtime {
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
.build()
|
.build()
|
||||||
@@ -156,49 +154,6 @@ fn runtime_gone() {
|
|||||||
.is_cancelled());
|
.is_cancelled());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This ensures that `join_next` works correctly when the coop budget is
|
|
||||||
// exhausted.
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
|
||||||
async fn join_set_coop() {
|
|
||||||
// Large enough to trigger coop.
|
|
||||||
const TASK_NUM: u32 = 1000;
|
|
||||||
|
|
||||||
static SEM: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(0);
|
|
||||||
|
|
||||||
let mut set = JoinSet::new();
|
|
||||||
|
|
||||||
for _ in 0..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();
|
|
||||||
|
|
||||||
let mut count = 0;
|
|
||||||
let mut coop_count = 0;
|
|
||||||
loop {
|
|
||||||
match set.join_next().now_or_never() {
|
|
||||||
Some(Some(Ok(()))) => {}
|
|
||||||
Some(Some(Err(err))) => panic!("failed: {}", err),
|
|
||||||
None => {
|
|
||||||
coop_count += 1;
|
|
||||||
tokio::task::yield_now().await;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Some(None) => break,
|
|
||||||
}
|
|
||||||
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
assert!(coop_count >= 1);
|
|
||||||
assert_eq!(count, TASK_NUM);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(start_paused = true)]
|
#[tokio::test(start_paused = true)]
|
||||||
async fn abort_all() {
|
async fn abort_all() {
|
||||||
let mut set: JoinSet<()> = JoinSet::new();
|
let mut set: JoinSet<()> = JoinSet::new();
|
||||||
@@ -228,3 +183,53 @@ async fn abort_all() {
|
|||||||
assert_eq!(count, 10);
|
assert_eq!(count, 10);
|
||||||
assert_eq!(set.len(), 0);
|
assert_eq!(set.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parking_lot")]
|
||||||
|
mod parking_lot {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
|
||||||
|
// This ensures that `join_next` works correctly when the coop budget is
|
||||||
|
// exhausted.
|
||||||
|
#[tokio::test(flavor = "current_thread")]
|
||||||
|
async fn join_set_coop() {
|
||||||
|
// Large enough to trigger coop.
|
||||||
|
const TASK_NUM: u32 = 1000;
|
||||||
|
|
||||||
|
static SEM: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(0);
|
||||||
|
|
||||||
|
let mut set = JoinSet::new();
|
||||||
|
|
||||||
|
for _ in 0..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();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
let mut coop_count = 0;
|
||||||
|
loop {
|
||||||
|
match set.join_next().now_or_never() {
|
||||||
|
Some(Some(Ok(()))) => {}
|
||||||
|
Some(Some(Err(err))) => panic!("failed: {}", err),
|
||||||
|
None => {
|
||||||
|
coop_count += 1;
|
||||||
|
tokio::task::yield_now().await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(None) => break,
|
||||||
|
}
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
assert!(coop_count >= 1);
|
||||||
|
assert_eq!(count, TASK_NUM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user