shed/fbthrift_ext/util/lib.rs (31 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory
* of this source tree.
*/
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::{Mutex, MutexGuard};
/// Future for the [`poll_with_lock`] function.
pub struct PollWithLock<'a, T, F, E>
where
F: FnMut(&mut MutexGuard<'a, T>, &mut Context<'_>) -> Poll<Result<(), E>> + Unpin,
{
lock: &'a Mutex<T>,
f: F,
}
/// Creates a new future that supports polling another future behind [`tokio::sync::Mutex`].
///
/// When some future-like struct is behind a lock, polling with the mutex locked easily creates
/// deadlock since no one else will be able to make progress on the lock-protected struct.
///
/// Note the closure should return a `Poll<Result<(), E>>` instead of `Poll<()>`. This allows the
/// closure to surface errors happened in polling.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use std::task::Poll;
/// use tokio::sync::Mutex;
/// use fbthrift_util::poll_with_lock;
///
/// struct Foobar(i32);
/// let lock = Mutex::new(Foobar(132));
///
/// let locked_future = poll_with_lock(
/// &lock,
/// |_locked, _ctx| Poll::Ready(Result::<(), ()>::Ok(()))
/// );
///
/// assert_eq!(locked_future.await.unwrap().0, 132);
/// # }
/// ```
pub fn poll_with_lock<'a, T, F, E>(lock: &'a Mutex<T>, f: F) -> PollWithLock<'a, T, F, E>
where
F: FnMut(&mut MutexGuard<'a, T>, &mut Context<'_>) -> Poll<Result<(), E>> + Unpin,
{
PollWithLock { lock, f }
}
impl<'a, T, F, E> Future for PollWithLock<'a, T, F, E>
where
F: FnMut(&mut MutexGuard<'a, T>, &mut Context<'_>) -> Poll<Result<(), E>> + Unpin,
{
type Output = Result<MutexGuard<'a, T>, E>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let mut fut = Box::pin(self.lock.lock());
match fut.as_mut().poll(ctx) {
Poll::Ready(mut locked) => match (&mut self.f)(&mut locked, ctx) {
Poll::Ready(Ok(())) => Poll::Ready(Ok(locked)),
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => Poll::Pending,
},
Poll::Pending => Poll::Pending,
}
}
}