#future #oneshot-channel #mutex #borrowing #resources #async

potential

Borrowing for futures - Potential enables interior mutation using mutex and asynchronous borrowing (leasing) through a oneshot channel

5 stable releases

2.2.1 Sep 19, 2020
2.2.0 Sep 16, 2020
2.1.0 Sep 14, 2020
2.0.0 Sep 14, 2020
1.0.0 Sep 11, 2020

#603 in Concurrency

Download history 40/week @ 2024-07-22 30/week @ 2024-07-29 33/week @ 2024-08-05 24/week @ 2024-08-12 23/week @ 2024-08-19 33/week @ 2024-08-26 24/week @ 2024-09-02 24/week @ 2024-09-09 10/week @ 2024-09-16 43/week @ 2024-09-23 14/week @ 2024-09-30 84/week @ 2024-10-07 33/week @ 2024-10-14 25/week @ 2024-10-21 12/week @ 2024-10-28 18/week @ 2024-11-04

97 downloads per month
Used in 3 crates (via samotop-delivery)

MIT/Apache

25KB
291 lines

Build Status Maintenance

potential 2.2.1

Borrowing for futures - a bit like Option, but async and with a return channel. A bit like a Mutex, compensating for MutexGuard not being safe to send. The item is moved (leased) from the owner Potential into the Lease. When the Lease is dropped, the item is sent back to the owner. Owner of the Potential can await the return of the leased item.

It is geared towards asynchronous servers. The use case is that some resources can or should be reused, such as TCP connections or crypto configuration or backend storage but they need to be sent off with futures where there is no track of the future of the shared resource unless &mut is used so single copy is guaranteed, Mutex is used with a lifetime bound for the future or you use Potential which allows you not to bind the future lifetime to the source. Still, only one running future/task can use the resource at a time. The owner can thus control the concurrency which is by default 1 and can be increased by resetting the Potential to a new value.

If the lease is not dropped properly (such as if you mem::forget() it or when its thread panics badly), the Potential calls will be stuck forever. If you suspect that this may happen, await with a timeout to be able to recover.

Sync + Send future example

Notice the mutation of String through immutable reference, initializing the potential through a lease, and that futures are Sync + Send. Futures on a lease naturally serialize (are mutually exclusive).

use potential::Potential;
use std::future::Future;
use std::task::Poll;
use std::pin::Pin;
#[derive(Default)]
struct Trial {
    inner: Potential<String>
}
impl Trial {
    async fn back_to_the_future(&self, item:String) -> usize {
        let mut lease = match self.inner.lease().await {
            Ok(lease) => lease,
            Err(gone) => gone.set(String::new())
        };
        // here some other async work...
        lease.extend(item.chars());
        lease.len()
    }
}
async fn test(trial: &Trial) -> String {
    let fut2 = trial.back_to_the_future(" and then".to_owned());
    let fut1 = trial.back_to_the_future("now".to_owned());
    assert_eq!(fut1.await, 3);
    assert_eq!(fut2.await, 12);
    trial.inner.lease().await.expect("set").clone()
}
let trial = Trial::default();
let mut fut = test(&trial);
assert_eq!(Box::pin(fut).as_mut().poll(&mut cx()), Poll::Ready("now and then".to_owned()));

is_sync_and_send(test(&trial));
fn is_sync_and_send<T: Sync + Send>(_: T) {};
fn cx() -> std::task::Context<'static> {
    let waker = futures_util::task::noop_waker_ref();
    let cx = std::task::Context::from_waker(waker);
    cx
};

Example: 'static + Sync + Send future with Arc

Not sure how to pull this off with async fn sugar. Suggestions welcome. Self must not be captured by the future for this to work.

use potential::Potential;
use std::sync::Arc;
use std::future::Future;
#[derive(Default)]
struct Trial {
    inner: Arc<Potential<String>>
}
impl Trial {
    fn back_to_the_static_future(&self, item:String) -> Box<dyn Future<Output = usize> + 'static + Send + Sync> {
        let inner = self.inner.clone();
        let fut = async move {
            let mut lease = match inner.lease_on_arc().await {
                Ok(lease) => lease,
                Err(gone) => gone.set(String::new())
            };
            // here some other async work...
            lease.extend(item.chars());
            lease.len()
        };
        Box::new(fut)
    }
}
let trial = Trial::default();
let fut = trial.back_to_the_static_future("now".to_owned());
is_sync_and_send_and_static(fut);
fn is_sync_and_send_and_static<T: Sync + Send + 'static>(_: T) {};

License

MIT OR Apache-2.0

Contribution

Unless you explicitly state otherwise, any contribution submitted for inclusion in samotop projects by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.

Dependencies

~0.7–1MB
~17K SLoC