use std::{
pin::Pin,
task::{Context, Poll},
};
#[cfg(doc)]
use super::Pool;
use super::{
Arc, Async, AsyncBuilder, Object, PoolInner, Position, Sync, SyncBuilder,
TryAcquireError, hint,
};
#[derive(Debug, PartialEq)]
pub enum GetError<B> {
Closed,
Builder(B),
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Get<T, B> {
pool_inner: Option<Arc<PoolInner<T, B>>>,
}
impl<T, B> Get<T, B> {
pub(super) fn new(inner: &Arc<PoolInner<T, B>>) -> Self {
Self { pool_inner: Some(inner.clone()) }
}
unsafe fn ready_ok<E>(
pool_inner: &mut Option<Arc<PoolInner<T, B>>>,
position: Position,
object: T,
) -> Poll<Result<Object<T, B>, E>> {
Poll::Ready(Ok(Object::new(
position,
object,
unsafe { pool_inner.take().unwrap_unchecked() },
)))
}
fn ready_builder_error<E>(
builder_error: E,
) -> Poll<Result<Object<T, B>, GetError<E>>> {
Poll::Ready(Err(GetError::Builder(builder_error)))
}
fn ready_closed_error<E>() -> Poll<Result<Object<T, B>, GetError<E>>> {
Poll::Ready(Err(GetError::Closed))
}
fn pending<P>(
pool_inner: &Arc<PoolInner<T, B>>,
context: &Context,
) -> Poll<P> {
let waker = context.waker().clone();
let _ = pool_inner.get_wakers_sender.send(waker);
Poll::Pending
}
}
impl<T, B> Future for Get<T, Sync<B>>
where
B: SyncBuilder<T>,
{
type Output = Result<Object<T, Sync<B>>, GetError<B::Error>>;
fn poll(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Self::Output> {
if self.pool_inner.is_none() {
hint::cold_path();
unreachable!(
"The `Get` future has already been resolved: it is violating the contract of `Future`"
);
}
unsafe {
let pool_inner = self.pool_inner.as_ref().unwrap_unchecked();
match pool_inner.try_acquire() {
Ok((position, maybe_object_value)) => {
match pool_inner.build_object_value_if_necessary(
&position,
maybe_object_value,
) {
Ok(object_value) => Get::ready_ok(
&mut self.pool_inner,
position,
object_value,
),
Err(builder_error) => {
Get::ready_builder_error(builder_error)
}
}
}
Err(TryAcquireError::Closed) => Get::ready_closed_error(),
Err(TryAcquireError::AllSlotsOccupied) => {
Get::pending(pool_inner, context)
}
}
}
}
}
impl<T, B> IntoFuture for Get<T, Async<B>>
where
T: 'static,
B: AsyncBuilder<T> + 'static,
{
type Output = Result<Object<T, Async<B>>, GetError<B::Error>>;
type IntoFuture = AsyncGet<T, Async<B>>;
fn into_future(self) -> Self::IntoFuture {
AsyncGet { pool_inner: self.pool_inner, builder_state: None }
}
}
pub struct AsyncGet<T, B>
where
B: AsyncBuilder<T>,
{
pool_inner: Option<Arc<PoolInner<T, B>>>,
#[allow(clippy::type_complexity)]
builder_state:
Option<(Position, Pin<Box<dyn Future<Output = Result<T, B::Error>>>>)>,
}
impl<T, B> AsyncGet<T, B>
where
B: AsyncBuilder<T>,
{
#[allow(clippy::type_complexity)]
unsafe fn poll_with_object_builder(
&mut self,
position: Position,
mut object_builder_future: Pin<
Box<dyn Future<Output = Result<T, B::Error>>>,
>,
context: &mut Context,
) -> Poll<Result<Object<T, B>, GetError<B::Error>>> {
unsafe {
match object_builder_future.as_mut().poll(context) {
Poll::Ready(Ok(object)) => {
Get::ready_ok(&mut self.pool_inner, position, object)
}
Poll::Ready(Err(builder_error)) => {
Get::ready_builder_error(builder_error)
}
Poll::Pending => {
self.builder_state
.replace((position, object_builder_future));
Get::pending(
self.pool_inner.as_ref().unwrap_unchecked(),
context,
)
}
}
}
}
}
impl<T, B> Future for AsyncGet<T, Async<B>>
where
T: 'static,
B: AsyncBuilder<T> + 'static,
{
type Output = Result<Object<T, Async<B>>, GetError<B::Error>>;
fn poll(
mut self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Self::Output> {
if self.pool_inner.is_none() {
hint::cold_path();
unreachable!(
"The `Get` future has already been resolved: it is violating the contract of `Future`"
);
}
unsafe {
if let Some((position, object_builder_future)) =
self.builder_state.take()
{
return self.poll_with_object_builder(
position,
object_builder_future,
context,
);
}
let pool_inner = self.pool_inner.as_ref().unwrap_unchecked();
match pool_inner.try_acquire() {
Ok((position, maybe_object_value)) => {
let object_builder_future = Box::pin({
let pool_inner = pool_inner.clone();
async move {
pool_inner
.build_object_value_if_necessary(
position,
maybe_object_value,
)
.await
}
});
self.poll_with_object_builder(
position,
object_builder_future,
context,
)
}
Err(TryAcquireError::Closed) => Get::ready_closed_error(),
Err(TryAcquireError::AllSlotsOccupied) => {
Get::pending(pool_inner, context)
}
}
}
}
}