Runtime-agnostic async wrappers for photon-ring pub/sub channels.
photon-ring's publisher has no waker infrastructure -- it writes to a slot, stores the stamp, and advances the cursor. There is no notification mechanism for async consumers.
This crate bridges the gap with yield-based polling: the async subscriber tries spin_budget synchronous try_recv() calls per poll, then yields to the executor. This is cooperative spin-polling, not event-driven wakeup. The trade-off: one executor scheduling round-trip (~200-500 ns) per empty yield, and other tasks run between polls -- but the waiting task never sleeps.
Idle cost. A task awaiting an idle channel is re-polled continuously and holds one core at 100% until a message arrives. Use this crate for streams that are rarely quiet. For idle-heavy workloads, run the subscriber on a dedicated thread (photon_ring::topology::Consumer) and hand results to your runtime through its own notification-based channel.
No tokio, async-std, or any runtime dependency. Uses only core::task and core::future::poll_fn.
let (mut pub_, subs) = photon_ring::channel::<u64>(64);
let mut async_sub = photon_ring_async::AsyncSubscriber::new(subs.subscribe());
pub_.publish(42);
let value = async_sub.recv().await;
assert_eq!(value, 42);// Low budget: yields quickly, good for mixed workloads
let mut sub = photon_ring_async::AsyncSubscriber::with_spin_budget(subs.subscribe(), 4);
// High budget: lower latency, holds executor thread longer
let mut sub = photon_ring_async::AsyncSubscriber::with_spin_budget(subs.subscribe(), 1024);let mut buf = [0u64; 256];
let count = async_sub.recv_batch(&mut buf).await;
// buf[..count] contains the received messagesMIT OR Apache-2.0