use core::future::{Future, poll_fn};
use core::pin::{Pin, pin};
use core::task::{Context, Poll};
use std::sync::Arc;
use std::time::Duration;
use crate::rt::context;
use crate::rt::sched::Sched;
use crate::rt::shard::Shard;
use crate::sync::atomic_waker::AtomicWaker;
use crate::sync::internal::{AtomicU8, Ordering};
const ASKED: u8 = 0;
const LENT: u8 = 1;
const BACK: u8 = 2;
const GONE: u8 = 3;
pub(crate) struct Loan {
state: AtomicU8,
borrower: AtomicWaker,
lender: AtomicWaker,
}
impl Loan {
fn new() -> Self {
Self {
state: AtomicU8::new(ASKED),
borrower: AtomicWaker::new(),
lender: AtomicWaker::new(),
}
}
fn lend(&self) -> bool {
let lent = self
.state
.compare_exchange(ASKED, LENT, Ordering::AcqRel, Ordering::Acquire)
.is_ok();
if lent {
self.borrower.wake();
}
lent
}
fn wait_back(&self) {
crate::sync::internal::block_on(poll_fn(|cx| {
self.lender.register(cx.waker());
if self.state.load(Ordering::Acquire) == BACK {
Poll::Ready(())
} else {
Poll::Pending
}
}));
}
pub(crate) fn give_back(&self) {
self.state.store(BACK, Ordering::Release);
self.lender.wake();
}
}
pub(crate) fn borrow(shard: &Shard) -> Option<Arc<Loan>> {
let monitor = shard.monitor()?;
let mut closing = pin!(monitor.closing());
closing.as_mut().enable();
if monitor.is_closed() {
return None;
}
let loan = Arc::new(Loan::new());
let lend = Lend {
loan: Arc::clone(&loan),
tick: monitor.tick(),
deferred: None,
};
let (runnable, task) =
crate::rt::task::spawn(lend, Sched::<false>::uncounted(Arc::clone(&shard.shared)));
task.detach();
shard.shared.push_remote(runnable);
let lent = crate::sync::internal::block_on(poll_fn(|cx| {
loan.borrower.register(cx.waker());
if loan.state.load(Ordering::Acquire) == LENT {
return Poll::Ready(true);
}
if closing.as_mut().poll(cx).is_ready() {
let gone =
loan.state
.compare_exchange(ASKED, GONE, Ordering::AcqRel, Ordering::Acquire);
return Poll::Ready(gone.is_err());
}
Poll::Pending
}));
lent.then_some(loan)
}
struct Lend {
loan: Arc<Loan>,
tick: Duration,
deferred: Option<Pin<Box<crate::time::Sleep>>>,
}
impl Future for Lend {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.get_mut();
if let Some(wait) = this.deferred.as_mut() {
if wait.as_mut().poll(cx).is_pending() {
return Poll::Pending;
}
this.deferred = None;
}
let held = context::current_held();
let Some(shard) = held.shard() else {
cx.waker().wake_by_ref();
return Poll::Pending;
};
if context::lends_a_value() {
let mut wait = Box::pin(crate::time::sleep(this.tick));
if wait.as_mut().poll(cx).is_pending() {
this.deferred = Some(wait);
} else {
cx.waker().wake_by_ref();
}
return Poll::Pending;
}
context::without_shard(|| shard.hand_over_queues());
if this.loan.lend() {
this.loan.wait_back();
}
Poll::Ready(())
}
}