use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::Mutex;
use std::task::{Context, Poll, Waker};
use std::time::Duration;
use crate::blocking_spsc_ring::{BlockingError, BlockingSpscRing};
use crate::shared_ring::RingError;
pub struct AsyncSpscRing {
inner: Arc<BlockingSpscRing>,
}
impl AsyncSpscRing {
pub fn new(inner: Arc<BlockingSpscRing>) -> Self {
Self { inner }
}
pub fn recv(&self, timeout: Duration) -> AsyncRecv {
AsyncRecv {
ring: Arc::clone(&self.inner),
state: Arc::new(Mutex::new(SlotState::Pending)),
timeout,
spawned: false,
}
}
pub fn send(&self, payload: Vec<u8>, timeout: Duration) -> AsyncSend {
AsyncSend {
ring: Arc::clone(&self.inner),
state: Arc::new(Mutex::new(SlotState::Pending)),
timeout,
payload: Some(payload),
spawned: false,
}
}
pub fn inner(&self) -> &Arc<BlockingSpscRing> { &self.inner }
}
enum SlotState<T> {
Pending,
Ready(T),
Parked(Waker),
}
type RecvSlot = Arc<Mutex<SlotState<Result<Vec<u8>, BlockingError>>>>;
type SendSlot = Arc<Mutex<SlotState<Result<(), BlockingError>>>>;
pub struct AsyncRecv {
ring: Arc<BlockingSpscRing>,
state: RecvSlot,
timeout: Duration,
spawned: bool,
}
impl Future for AsyncRecv {
type Output = Result<Vec<u8>, BlockingError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if !this.spawned {
let mut buf = vec![0u8; 64];
match this.ring.try_pop(&mut buf) {
Ok(n) => {
buf.truncate(n);
return Poll::Ready(Ok(buf));
}
Err(RingError::Empty) => {}
Err(e) => return Poll::Ready(Err(BlockingError::Ring(e))),
}
}
let mut guard = this.state.lock().unwrap();
match &mut *guard {
SlotState::Ready(_) => {
let taken = std::mem::replace(&mut *guard, SlotState::Pending);
match taken {
SlotState::Ready(r) => return Poll::Ready(r),
_ => unreachable!(),
}
}
SlotState::Pending | SlotState::Parked(_) => {
*guard = SlotState::Parked(cx.waker().clone());
}
}
drop(guard);
if !this.spawned {
this.spawned = true;
let ring = Arc::clone(&this.ring);
let state = Arc::clone(&this.state);
let timeout = this.timeout;
std::thread::spawn(move || {
let mut buf = vec![0u8; 64];
let r = ring.recv_blocking(&mut buf, Some(timeout)).map(|n| {
buf.truncate(n);
buf
});
finish_slot(&state, r);
});
}
Poll::Pending
}
}
pub struct AsyncSend {
ring: Arc<BlockingSpscRing>,
state: SendSlot,
timeout: Duration,
payload: Option<Vec<u8>>,
spawned: bool,
}
impl Future for AsyncSend {
type Output = Result<(), BlockingError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if !this.spawned {
let payload_ref = this.payload.as_ref().expect("payload taken twice");
match this.ring.try_push(payload_ref) {
Ok(()) => return Poll::Ready(Ok(())),
Err(RingError::Full) => {}
Err(e) => return Poll::Ready(Err(BlockingError::Ring(e))),
}
}
let mut guard = this.state.lock().unwrap();
match &mut *guard {
SlotState::Ready(_) => {
let taken = std::mem::replace(&mut *guard, SlotState::Pending);
match taken {
SlotState::Ready(r) => return Poll::Ready(r),
_ => unreachable!(),
}
}
SlotState::Pending | SlotState::Parked(_) => {
*guard = SlotState::Parked(cx.waker().clone());
}
}
drop(guard);
if !this.spawned {
this.spawned = true;
let ring = Arc::clone(&this.ring);
let state = Arc::clone(&this.state);
let timeout = this.timeout;
let payload = this.payload.take().expect("payload taken twice");
std::thread::spawn(move || {
let r = ring.send_blocking(&payload, Some(timeout));
finish_slot(&state, r);
});
}
Poll::Pending
}
}
fn finish_slot<T>(state: &Arc<Mutex<SlotState<T>>>, value: T) {
let waker_to_fire = {
let mut guard = state.lock().unwrap();
let prev = std::mem::replace(&mut *guard, SlotState::Ready(value));
match prev {
SlotState::Parked(w) => Some(w),
_ => None,
}
};
if let Some(w) = waker_to_fire {
w.wake();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::Wake;
struct TestWaker {
woken: std::sync::Mutex<bool>,
cv: std::sync::Condvar,
}
impl Wake for TestWaker {
fn wake(self: Arc<Self>) {
let mut g = self.woken.lock().unwrap();
*g = true;
self.cv.notify_one();
}
}
fn block_on<F: Future>(mut fut: F) -> F::Output {
let waker_inner = Arc::new(TestWaker {
woken: std::sync::Mutex::new(true),
cv: std::sync::Condvar::new(),
});
let waker: Waker = Arc::clone(&waker_inner).into();
let mut cx = Context::from_waker(&waker);
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
loop {
{
let mut g = waker_inner.woken.lock().unwrap();
while !*g {
g = waker_inner.cv.wait(g).unwrap();
}
*g = false;
}
match fut.as_mut().poll(&mut cx) {
Poll::Ready(v) => return v,
Poll::Pending => continue,
}
}
}
#[test]
fn recv_returns_immediately_when_ring_has_item() {
let ring = Arc::new(BlockingSpscRing::create_anon(4).expect("ring"));
let mut payload = [0u8; 56];
payload[..8].copy_from_slice(&42u64.to_le_bytes());
ring.try_push(&payload).expect("push");
let adapter = AsyncSpscRing::new(Arc::clone(&ring));
let got = block_on(adapter.recv(Duration::from_secs(1))).unwrap();
let val = u64::from_le_bytes(got[..8].try_into().unwrap());
assert_eq!(val, 42);
}
#[test]
fn recv_parks_then_completes_when_producer_pushes() {
let ring = Arc::new(BlockingSpscRing::create_anon(4).expect("ring"));
let r2 = Arc::clone(&ring);
let pushed = Arc::new(AtomicBool::new(false));
let pushed2 = Arc::clone(&pushed);
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(40));
let mut payload = [0u8; 56];
payload[..8].copy_from_slice(&7u64.to_le_bytes());
pushed2.store(true, Ordering::Release);
r2.try_push(&payload).expect("push");
});
let adapter = AsyncSpscRing::new(Arc::clone(&ring));
let got = block_on(adapter.recv(Duration::from_secs(2))).unwrap();
assert!(pushed.load(Ordering::Acquire), "producer ran");
let val = u64::from_le_bytes(got[..8].try_into().unwrap());
assert_eq!(val, 7);
}
#[test]
fn recv_times_out_when_no_producer() {
let ring = Arc::new(BlockingSpscRing::create_anon(4).expect("ring"));
let adapter = AsyncSpscRing::new(Arc::clone(&ring));
let t0 = std::time::Instant::now();
let r = block_on(adapter.recv(Duration::from_millis(80)));
assert!(matches!(r, Err(BlockingError::Timeout)));
assert!(t0.elapsed() >= Duration::from_millis(60));
}
#[test]
fn send_completes_immediately_when_ring_not_full() {
let ring = Arc::new(BlockingSpscRing::create_anon(4).expect("ring"));
let adapter = AsyncSpscRing::new(Arc::clone(&ring));
let payload = (12345u64).to_le_bytes().to_vec();
block_on(adapter.send(payload, Duration::from_secs(1))).unwrap();
let mut buf = [0u8; 64];
let n = ring.try_pop(&mut buf).expect("pop");
let v = u64::from_le_bytes(buf[..8].try_into().unwrap());
assert_eq!(v, 12345);
assert!(n >= 8);
}
}