Skip to main content

apalis_core/backend/
future.rs

1use std::{
2    fmt::Debug,
3    sync::Mutex,
4    task::{Context, Poll},
5};
6
7use futures_core::future::BoxFuture;
8
9/// Wraps a `!Sync` boxed future so it can live inside a `Sync` container.
10/// Safe because we only ever poll it through `&mut self`, which never
11/// contends the lock.
12pub struct BoxSyncFuture<T>(Mutex<BoxFuture<'static, T>>);
13
14impl<T> Debug for BoxSyncFuture<T> {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        f.debug_struct("BoxSyncFuture { .. }").finish()
17    }
18}
19
20impl<T> BoxSyncFuture<T> {
21    /// Compose a Boxed Future that is Sync
22    #[must_use]
23    pub fn new(fut: BoxFuture<'static, T>) -> Self {
24        Self(Mutex::new(fut))
25    }
26
27    /// Call poll directly to avoid pinning twice
28    pub fn poll_unpin(&mut self, cx: &mut Context<'_>) -> Poll<T> {
29        // get_mut() never actually locks — we already have &mut self.
30        self.0.get_mut().unwrap().as_mut().poll(cx)
31    }
32}
33
34impl<T> From<BoxFuture<'static, T>> for BoxSyncFuture<T> {
35    fn from(fut: BoxFuture<'static, T>) -> Self {
36        Self::new(fut)
37    }
38}