use crate::runtime::{self, Runtime};
use std::sync::Arc;
use std::time::Duration;
#[test]
fn blocking_shutdown() {
loom::model(|| {
let v = Arc::new(());
let rt = mk_runtime(1);
{
let _enter = rt.enter();
for _ in 0..2 {
let v = v.clone();
crate::task::spawn_blocking(move || {
assert!(1 < Arc::strong_count(&v));
});
}
}
drop(rt);
assert_eq!(1, Arc::strong_count(&v));
});
}
#[test]
fn spawn_mandatory_blocking_should_always_run() {
use crate::runtime::tests::loom_oneshot;
loom::model(|| {
let rt = runtime::Builder::new_current_thread().build().unwrap();
let (tx, rx) = loom_oneshot::channel();
let _enter = rt.enter();
runtime::spawn_blocking(|| {});
runtime::spawn_mandatory_blocking(move || {
let _ = tx.send(());
})
.unwrap();
drop(rt);
let () = rx.recv();
});
}
#[test]
fn spawn_mandatory_blocking_should_run_even_when_shutting_down_from_other_thread() {
use crate::runtime::tests::loom_oneshot;
loom::model(|| {
let rt = runtime::Builder::new_current_thread().build().unwrap();
let handle = rt.handle().clone();
{
loom::thread::spawn(move || {
drop(rt);
});
}
let _enter = handle.enter();
let (tx, rx) = loom_oneshot::channel();
let handle = runtime::spawn_mandatory_blocking(move || {
let _ = tx.send(());
});
if handle.is_some() {
let () = rx.recv();
}
});
}
#[test]
fn mandatory_blocking_io_write_should_always_run() {
use crate::io::blocking::Blocking;
use crate::io::AsyncWriteExt;
use crate::runtime::tests::loom_oneshot;
use std::io;
struct Writer(Option<loom_oneshot::Sender<()>>);
impl io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if let Some(tx) = self.0.take() {
tx.send(());
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
loom::model(|| {
let rt = runtime::Builder::new_current_thread().build().unwrap();
let (tx, rx) = loom_oneshot::channel();
let _enter = rt.enter();
runtime::spawn_blocking(|| {});
let mut writer = unsafe { Blocking::new_mandatory(Writer(Some(tx))) };
loom::future::block_on(writer.write_all(b"hello")).unwrap();
drop(rt);
let () = rx.recv();
});
}
#[test]
fn spawn_blocking_when_paused() {
loom::model(|| {
let rt = crate::runtime::Builder::new_current_thread()
.enable_time()
.start_paused(true)
.build()
.unwrap();
let handle = rt.handle();
let _enter = handle.enter();
let a = crate::task::spawn_blocking(|| {});
let b = crate::task::spawn_blocking(|| {});
rt.block_on(crate::time::timeout(Duration::from_millis(1), async move {
a.await.expect("blocking task should finish");
b.await.expect("blocking task should finish");
}))
.expect("timeout should not trigger");
});
}
#[test]
fn spawn_blocking_then_shutdown() {
loom::model(|| {
let rt = crate::runtime::Builder::new_current_thread()
.max_blocking_threads(1)
.thread_keep_alive(Duration::from_secs(7200)) .build()
.unwrap();
let rt_hdl = rt.handle().clone();
let jh0 = rt_hdl.spawn_blocking(|| {});
loom::future::block_on(jh0).unwrap();
let jh3 = rt_hdl.spawn_blocking(|| {});
drop(rt);
let _ = loom::future::block_on(jh3);
});
}
#[test]
fn spawn_blocking_at_thread_cap_runs() {
loom::model(|| {
let rt = crate::runtime::Builder::new_current_thread()
.max_blocking_threads(1)
.thread_keep_alive(Duration::from_secs(7200)) .build()
.unwrap();
let rt_hdl = rt.handle().clone();
let jh0 = rt_hdl.spawn_blocking(|| {});
loom::future::block_on(jh0).unwrap();
let jh1 = rt_hdl.spawn_blocking(|| {});
loom::future::block_on(jh1).unwrap();
drop(rt);
});
}
#[test]
fn spawn_blocking_racing_shutdown_resolves() {
loom::model(|| {
let rt = crate::runtime::Builder::new_current_thread()
.build()
.unwrap();
let handle = rt.handle().clone();
let spawner = loom::thread::spawn(move || handle.spawn_blocking(|| {}));
drop(rt);
let jh = spawner.join().unwrap();
let _ = loom::future::block_on(jh);
});
}
fn mk_runtime(num_threads: usize) -> Runtime {
runtime::Builder::new_multi_thread()
.worker_threads(num_threads)
.build()
.unwrap()
}