swctx 0.3.0

One-shot channel with some special semantics.
Documentation
use std::{
  future::Future,
  pin::Pin,
  sync::Arc,
  task::{Context, Poll}
};

use crate::err::Error;

use super::{Shared, State};


/// End-point used to wait for a value to be sent from the paired
/// [`SetCtx`](super::SetCtx).
#[repr(transparent)]
pub struct WaitCtx<T, S, E>(pub(crate) Arc<Shared<T, S, E>>);

impl<T, S, E> WaitCtx<T, S, E> {
  /// Wait for the paired [`SetCtx`](super::SetCtx) to set a value or fail.
  ///
  /// # Errors
  /// Returns application-specific error wrapped in an [`Error::App`] if the
  /// `SetCtx` reported failure.
  pub fn wait(self) -> Result<T, Error<S, E>> {
    let mut inner = self.0.inner.lock();
    loop {
      match inner.state {
        State::Waiting => {
          self.0.signal.wait(&mut inner);
        }
        State::Data(_) => {
          let old = std::mem::replace(&mut inner.state, State::Finalized);
          drop(inner);
          let State::Data(data) = old else {
            unimplemented!("Unable to extract data");
          };
          break Ok(data);
        }
        State::Err(_) => {
          let old = std::mem::replace(&mut inner.state, State::Finalized);
          drop(inner);
          let State::Err(err) = old else {
            unimplemented!("Unable to extract error");
          };
          break Err(err);
        }
        State::Finalized => {
          // Shouldn't be possible
          unimplemented!("Unexpected state")
        }
      }
    }
  }

  /// Non-blocking attempt to get the get the stored value.
  ///
  /// Returns `Ok(Some(T))` if a value has been stored.  Returns `Ok(None)` if
  /// no value has been stored.
  ///
  /// # Errors
  /// Returns application-specific error wrapped in an [`Error::App`] if the
  /// [`SetCtx`](super::SetCtx) reported failure.
  ///
  /// # Panics
  /// This function will panic if called again after it has resolved to either
  /// data or error.
  pub fn try_get(&self) -> Result<Option<T>, Error<S, E>> {
    let mut inner = self.0.inner.lock();
    inner.try_get()
  }

  /// Return a `Future` that will wait for either data to be set or an error to
  /// occur.
  ///
  /// # Cancel safety
  /// The returned `Future` is cancel safe.
  ///
  /// # Panics
  /// This function will panic if called again after it has resolved to either
  /// data or error.
  #[must_use]
  pub const fn wait_async(&self) -> WaitFuture<T, S, E> {
    WaitFuture(self)
  }
}

impl<T, S, E> Drop for WaitCtx<T, S, E> {
  fn drop(&mut self) {
    let mut inner = self.0.inner.lock();
    inner.wctx_dropped = true;
  }
}

impl<T, S, E> Future for WaitCtx<T, S, E> {
  type Output = Result<T, Error<S, E>>;
  fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
    let mut inner = self.0.inner.lock();
    match inner.try_get() {
      Ok(Some(v)) => Poll::Ready(Ok(v)),
      Ok(None) => {
        inner.waker = Some(ctx.waker().clone());
        Poll::Pending
      }
      Err(e) => Poll::Ready(Err(e))
    }
  }
}


/// Used to wait for the paired [`SetCtx`](super::SetCtx) to set a value in an
/// `async` context.
// A reference to the `WaitCtx` is used (rather than a clone of its
// Arc<Shared>) to tie the lifetime of the `WaitCtx` and the `WaitFuture`
// together, to avoid `WaitFuture` outliving the `WaitCtx` (which wouldn't make
// sense, because dropping `WaitCtx` sets `wctx_dropped` in the shared
// context).
#[repr(transparent)]
pub struct WaitFuture<'wctx, T, S, E>(&'wctx WaitCtx<T, S, E>);

impl<'wctx, T, S, E> Future for WaitFuture<'wctx, T, S, E> {
  type Output = Result<T, Error<S, E>>;
  fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
    let mut inner = self.0 .0.inner.lock();
    match inner.try_get() {
      Ok(Some(v)) => Poll::Ready(Ok(v)),
      Ok(None) => {
        inner.waker = Some(ctx.waker().clone());
        Poll::Pending
      }
      Err(e) => Poll::Ready(Err(e))
    }
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :