use std::{future::Future, io, thread, time::Duration};
use crate::{sync, BatchError, Channel, Receiver, Sender};
pub fn spawn<
T: Channel + Send + 'static,
F: Future<Output = Result<(), BatchError<T>>> + Send + 'static,
>(
thread_name: impl Into<String>,
receiver: Receiver<T>,
on_batch: impl FnMut(T) -> F + Send + 'static,
) -> io::Result<thread::JoinHandle<()>>
where
T::Item: Send + 'static,
{
let receive = async move {
receiver
.exec(|delay| tokio::time::sleep(delay), on_batch)
.await
};
thread::Builder::new()
.name(thread_name.into())
.spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(receive);
})
}
pub fn blocking_flush<T: Channel>(sender: &Sender<T>, timeout: Duration) -> bool {
match tokio::runtime::Handle::try_current() {
Ok(handle) => handle.block_on(flush(sender, timeout)),
Err(_) => sync::blocking_flush(sender, timeout),
}
}
pub async fn flush<T: Channel>(sender: &Sender<T>, timeout: Duration) -> bool {
let (notifier, notified) = tokio::sync::oneshot::channel();
sender.when_flushed(move || {
let _ = notifier.send(());
});
wait(notified, timeout).await
}
pub fn blocking_send<T: Channel>(
sender: &Sender<T>,
msg: T::Item,
timeout: Duration,
) -> Result<(), BatchError<T::Item>> {
match tokio::runtime::Handle::try_current() {
Ok(handle) => handle.block_on(send(sender, msg, timeout)),
Err(_) => sync::blocking_send(sender, msg, timeout),
}
}
pub async fn send<T: Channel>(
sender: &Sender<T>,
msg: T::Item,
timeout: Duration,
) -> Result<(), BatchError<T::Item>> {
sender
.send_or_wait(msg, timeout, |sender, timeout| async move {
let (notifier, notified) = tokio::sync::oneshot::channel();
sender.when_empty(move || {
let _ = notifier.send(());
});
wait(notified, timeout).await;
})
.await
}
async fn wait(mut notified: tokio::sync::oneshot::Receiver<()>, timeout: Duration) -> bool {
if notified.try_recv().is_ok() {
return true;
}
if timeout == Duration::ZERO {
return false;
}
match tokio::time::timeout(timeout, notified).await {
Ok(Ok(())) => true,
Ok(Err(_)) => true,
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[tokio::test]
async fn async_send_recv_flush() {
let received = Arc::new(Mutex::new(0));
let (sender, receiver) = crate::bounded::<Vec<()>>(10);
let _ = spawn("test_receiver", receiver, {
let received = received.clone();
move |batch| {
let received = received.clone();
async move {
*received.lock().unwrap() += batch.len();
Ok(())
}
}
})
.unwrap();
for _ in 0..100 {
send(&sender, (), Duration::from_secs(1))
.await
.map_err(|_| "failed to send")
.unwrap();
}
flush(&sender, Duration::from_secs(1)).await;
assert_eq!(100, *received.lock().unwrap());
}
}