use busybeaver::{listener, work, Beaver, BeaverResult, FixedCountBuilder, WorkResult};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::time::Duration;
const TEST_TIMEOUT: Duration = Duration::from_millis(500);
const SHORT_SLEEP: Duration = Duration::from_millis(30);
fn require_send_sync<T: Send + Sync>() {}
#[tokio::test]
async fn test_beaver_is_send_and_sync_compile_requirement() {
require_send_sync::<Beaver>();
require_send_sync::<Arc<Beaver>>();
}
#[tokio::test]
async fn test_beaver_send_via_tokio_spawn() -> BeaverResult<()> {
let beaver = Arc::new(Beaver::new("send_spawn", 8));
let done = Arc::new(AtomicU32::new(0));
let task = FixedCountBuilder::new(work({
let done = Arc::clone(&done);
move || {
let d = Arc::clone(&done);
async move {
d.fetch_add(1, Ordering::SeqCst);
WorkResult::Done(())
}
}
}))
.count(1)
.build()?;
let beaver_clone = Arc::clone(&beaver);
let join = tokio::spawn(async move {
let _ = beaver_clone.enqueue(task).await;
});
let _ = tokio::time::timeout(TEST_TIMEOUT, join).await;
tokio::time::sleep(SHORT_SLEEP).await;
assert!(
done.load(Ordering::SeqCst) >= 1,
"task should run at least once"
);
beaver.cancel_all().await?;
beaver.destroy().await?;
Ok(())
}
#[tokio::test]
async fn test_beaver_send_via_std_thread() -> BeaverResult<()> {
let beaver = Arc::new(Beaver::new("send_thread", 8));
let done = Arc::new(AtomicU32::new(0));
let task = FixedCountBuilder::new(work({
let done = Arc::clone(&done);
move || {
let d = Arc::clone(&done);
async move {
d.fetch_add(1, Ordering::SeqCst);
WorkResult::Done(())
}
}
}))
.count(1)
.build()?;
let beaver_clone = Arc::clone(&beaver);
let handle = std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
let _ = beaver_clone.enqueue(task).await;
});
});
let _ = handle.join();
tokio::time::sleep(SHORT_SLEEP).await;
assert!(done.load(Ordering::SeqCst) >= 1);
beaver.cancel_all().await?;
beaver.destroy().await?;
Ok(())
}
#[tokio::test]
async fn test_beaver_sync_multiple_tasks_share_arc() -> BeaverResult<()> {
let beaver = Arc::new(Beaver::new("sync_shared", 32));
let counter = Arc::new(AtomicU32::new(0));
let n = 4usize;
let mut joins = Vec::with_capacity(n);
for _ in 0..n {
let beaver_clone = Arc::clone(&beaver);
let counter_clone = Arc::clone(&counter);
let j = tokio::spawn(async move {
let task = FixedCountBuilder::new(work(move || {
let c = Arc::clone(&counter_clone);
async move {
c.fetch_add(1, Ordering::SeqCst);
WorkResult::Done(())
}
}))
.count(1)
.build()
.unwrap();
beaver_clone.enqueue(task).await
});
joins.push(j);
}
for j in joins {
let _ = tokio::time::timeout(TEST_TIMEOUT, j).await;
}
tokio::time::sleep(SHORT_SLEEP).await;
assert!(
counter.load(Ordering::SeqCst) >= 1,
"at least one task should run"
);
let _ = tokio::time::timeout(TEST_TIMEOUT, beaver.cancel_all()).await;
beaver.destroy().await?;
Ok(())
}
#[tokio::test]
async fn test_beaver_send_sync_concurrent_enqueue_and_cancel() -> BeaverResult<()> {
let beaver = Arc::new(Beaver::new("send_sync_concurrent", 32));
let counter = Arc::new(AtomicU32::new(0));
let mut joins = Vec::new();
for i in 0..4 {
let beaver_clone = Arc::clone(&beaver);
let counter_clone = Arc::clone(&counter);
let j = tokio::spawn(async move {
let task = FixedCountBuilder::new(work(move || {
let c = Arc::clone(&counter_clone);
async move {
c.fetch_add(1, Ordering::SeqCst);
WorkResult::Done(())
}
}))
.count(1)
.tag(format!("t-{}", i))
.build()
.unwrap();
let _ = beaver_clone.enqueue(task).await;
});
joins.push(j);
}
let cancel_beaver = Arc::clone(&beaver);
let cancel_join = tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(20)).await;
let _ = cancel_beaver.cancel_all().await;
});
for j in joins {
let _ = tokio::time::timeout(TEST_TIMEOUT, j).await;
}
let _ = tokio::time::timeout(TEST_TIMEOUT, cancel_join).await;
tokio::time::sleep(Duration::from_millis(30)).await;
beaver.destroy().await?;
assert!(counter.load(Ordering::SeqCst) >= 1);
Ok(())
}
#[tokio::test]
async fn test_beaver_send_sync_main_and_spawn_share_arc() -> BeaverResult<()> {
let beaver = Arc::new(Beaver::new("main_and_spawn", 8));
let done = Arc::new(AtomicU32::new(0));
let task = FixedCountBuilder::new(work({
let done = Arc::clone(&done);
move || {
let d = Arc::clone(&done);
async move {
d.fetch_add(1, Ordering::SeqCst);
WorkResult::Done(())
}
}
}))
.count(1)
.listener(listener(|| {}, || {}))
.build()?;
let spawn_beaver = Arc::clone(&beaver);
let join = tokio::spawn(async move {
let _ = spawn_beaver.enqueue(task).await;
});
let _ = tokio::time::timeout(TEST_TIMEOUT, join).await;
tokio::time::sleep(SHORT_SLEEP).await;
beaver.cancel_all().await?;
beaver.destroy().await?;
assert!(done.load(Ordering::SeqCst) >= 1);
Ok(())
}
#[tokio::test]
async fn test_arc_beaver_can_be_sent_to_spawn_because_beaver_is_send() {
require_send_sync::<Beaver>();
let _: Arc<Beaver> = Arc::new(Beaver::new("doc_send", 8));
}
#[tokio::test]
async fn test_arc_beaver_can_be_shared_because_beaver_is_sync() {
require_send_sync::<Arc<Beaver>>();
}