use crate::{closed, lock, spawn_owned, Core, DiscoveryHandle};
use nadisc::managed::{DiscoveryError, ErrorCode, Result};
use std::{
future::Future,
sync::{Arc, Weak},
};
use tokio::{sync::OwnedSemaphorePermit, task::AbortHandle, time::Instant};
use tokio_util::sync::CancellationToken;
pub struct TransportLease {
core: Weak<Core>,
stop: CancellationToken,
permit: OwnedSemaphorePermit,
}
impl DiscoveryHandle {
pub fn reserve_transport(&self) -> Result<TransportLease> {
let core = self.core.upgrade().ok_or_else(closed)?;
let gate = lock(&core.gate);
if gate.failed || gate.close_started {
return Err(closed());
}
let permit = core.transports.clone().try_acquire_owned().map_err(|_| {
DiscoveryError::new(
ErrorCode::CapacityExceeded,
"owner transport task capacity reached",
)
})?;
Ok(TransportLease {
core: Arc::downgrade(&core),
stop: core.stop.child_token(),
permit,
})
}
}
impl TransportLease {
pub async fn cancelled(&self) {
self.stop.cancelled().await;
}
pub fn spawn(
self,
deadline: Instant,
future: impl Future<Output = ()> + Send + 'static,
) -> Result<AbortHandle> {
let core = self.core.upgrade().ok_or_else(closed)?;
let gate = lock(&core.gate);
if gate.failed || gate.close_started {
return Err(closed());
}
if deadline <= Instant::now() {
return Err(crate::timeout());
}
Ok(spawn_owned(&core, async move {
let _permit = self.permit;
tokio::select! {
biased;
_ = self.stop.cancelled() => {},
_ = tokio::time::sleep_until(deadline) => {},
_ = future => {},
}
}))
}
}