🗑Async-dropper is probably the least-worst ad-hoc AysncDrop implementation you've seen so far.

Overview

🗑 async-dropper

async-dropper is probably the least-worst ad-hoc AsyncDrop implementation you've seen, and it works in two ways:

  • async_dropper::simple is stolen nearly verbatim from this StackOverflow answer (thanks to paholg!)
  • async_dropper::derive provides a trait called AsyncDrop and corresponding derive macro, which try to use Default and PartialEq to determine when to async drop.

The code in this crate was most directly inspired by this StackOverflow thread on Async Drop and many other conversations:

Install

You must set features on this crate, as it works with async runtimes:

cargo add async-dropper --features tokio     # use tokio
cargo add async-dropper --features async-std # use async-std

If you're editing Cargo.toml by hand, choose one of the following lines:

[dependencies]
#async-dropper = { version = "0.1", features = [ "tokio" ] }
#async-dropper = { version = "0.1", features = [ "async-std" ] }

Warning
async-dropper does not allow using both async-std and tokio features at the same time (see the FAQ below).

Quickstart

async_dropper::simple

To use the "simple" version which uses a wrapper struct (AsyncDropper), see examples/async_drop_simple.rs:

Result<(), Box> { { let _example_obj = AsyncDropper::new(AsyncThing(String::from("test"))); eprintln!("here comes the (async) drop"); // drop will be triggered here, and it will take *however long it takes* // you could also call `drop(_example_obj)` } Ok(()) } ">
use std::{
    result::Result,
    time::Duration,
};

use async_dropper_simple::{AsyncDrop, AsyncDropper};
use async_trait::async_trait;

// NOTE: this example is rooted in crates/async-dropper

/// This object will be async-dropped (which must be wrapped in AsyncDropper)
#[derive(Default)]
struct AsyncThing(String);

#[async_trait]
impl AsyncDrop for AsyncThing {
    async fn async_drop(&mut self) {
        eprintln!("async dropping [{}]!", self.0);
        tokio::time::sleep(Duration::from_secs(2)).await;
        eprintln!("dropped [{}]!", self.0);
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    {
        let _example_obj = AsyncDropper::new(AsyncThing(String::from("test")));
        eprintln!("here comes the (async) drop");
        // drop will be triggered here, and it will take *however long it takes*
        // you could also call `drop(_example_obj)`
    }

    Ok(())
}

You can run the example and see the output:

cargo run --example async-drop-simple --features=tokio

async_dropper::derive

The derive macro is a novel (and possibly foolhardy) attempt to implement AsyncDrop without actually wrapping the existing struct.

async_dropper::derive uses Default and PartialEq to check if the struct in question is equivalent to it's default.

For this approach to work well your T should have cheap-to-create Defaults, and comparing a default value to an existing value should meaningfully differ (and identify an object that is no longer in use). Please think thoroughly about whether this model works for your use case.

For an example, see examples/async_drop.rs:

Duration { Duration::from_secs(5) // extended from default 3 seconds } // NOTE: below was not implemented since we want the default of DropFailAction::Continue // fn drop_fail_action(&self) -> DropFailAction; } #[tokio::main] async fn main() -> Result<(), Box> { { let _example_obj = AsyncThing(String::from("test")); eprintln!("here comes the (async) drop"); // drop will be triggered here // you could also call `drop(_example_obj)` } Ok(()) }">
use std::{
    result::Result,
    time::Duration,
};

use async_dropper::derive::AsyncDrop;
use async_trait::async_trait;

/// This object will be async-dropped
///
/// Objects that are dropped *must* implement [Default] and [PartialEq]
/// (so make members optional, hide them behind Rc/Arc as necessary)
#[derive(Debug, Default, PartialEq, Eq, AsyncDrop)]
struct AsyncThing(String);

/// Implementation of [AsyncDrop] that specifies the actual behavior
#[async_trait]
impl AsyncDrop for AsyncThing {
    // simulated work during async_drop
    async fn async_drop(&mut self) -> Result<(), AsyncDropError> {
        eprintln!("async dropping [{}]!", self.0);
        tokio::time::sleep(Duration::from_secs(2)).await;
        eprintln!("dropped [{}]!", self.0);
        Ok(())
    }

    // This function serves to indicate when async_drop behavior should *not* be performed
    // (i.e., if Self::default == Self, a drop must have occurred, or does not need to)
    fn reset(&mut self) {
        self.0 = String::default();
    }

    // How long we can allow async drop behavior to block
    fn drop_timeout(&self) -> Duration {
        Duration::from_secs(5) // extended from default 3 seconds
    }

    // NOTE: below was not implemented since we want the default of DropFailAction::Continue
    // fn drop_fail_action(&self) -> DropFailAction;
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    {
        let _example_obj = AsyncThing(String::from("test"));
        eprintln!("here comes the (async) drop");
        // drop will be triggered here
        // you could also call `drop(_example_obj)`
    }

    Ok(())
}

You can run the example and see the output:

cargo run --example async-drop --features=tokio

Supported environments

async-dropper works with the following async environments:

Name Supported?
Async w/ tokio ✅
Async w/ async-std ✅

FAQ

Why does async-dropper assume that I'm using either async-std or tokio

Because you probably are. If this is a problem for you, it can be changed, please file an issue.

What does async_dropper::derive cost?

There is waste introduced by async_dropper::derive, namely:

  • One Mutex-protected T::default() instance of your type, that exists as long as the program runs
  • One extra T::default() that is made of an individual T being dropped.

As a result, every drop you perform on a T will perform two drops -- one on a T::default() and another on your T, which has been converted to a T::default (via reset(&mut self)).

Development

To get started working on developing async-dropper, run the following just targets:

just setup build

To check that your changes are fine, you'll probably want to run:

just test

If you want to see the full list of targets available that you can run just without any arguments.

just

There are a few useful targets like just build-watch which will continuously build the project thanks to cargo watch.

Releasing

From the top level of this repository:

PUBLISH_CRATE=yes PKG= just release 

For example, to create the next semver patch release for async-dropper-simple:

PUBLISH_CRATE=yes PKG=async-dropper-simple just release patch

Contributing

Contributions are welcome! If you find a bug or an impovement that should be included in async-dropper, create an issue or open a pull request.

You might also like...
Simple async codec for rkyv. Reuses streaming buffer for maximum speed

rkyv_codec Simple async codec for rkyv. Reuses streaming buffer for maximum speed! This crate provides a makeshift adaptor for streaming &ArchivedObj

You can name anonymous Future from async fn without dyn or Box!

rename-future You can name anonymous Future from async fn without dyn or Box! PLEASE READ THIS THIS PROJECT NOT YET WELL TESTED! DON'T USE THIS IN PRO

The most fundamental type for async synchronization: an intrusive linked list of futures

wait-list This crate provides WaitList, the most fundamental type for async synchronization. WaitList is implemented as an intrusive linked list of fu

Ector is an open source async, no-alloc actor framework for embedded devices

Ector is an open source async, no-alloc actor framework for embedded devices. Ector is an open source async, no-alloc actor framework for embedded dev

Async variant of Tonic's `interceptor` function

Tonic Async Interceptor This crate contains AsyncInterceptor, an async variant of Tonic's Interceptor. Other than accepting an async interceptor funct

Use winit like the async runtime you've always wanted

async-winit Use winit like the async runtime you've always wanted. winit is actually asynchronous, contrary to popular belief; it's just not async. It

Rust library for one-time async initialization

async-lazy An async version of once_cell::sync::Lazy, std::sync::OnceLock or lazy_static. Uses tokio's sychronization primitives. This crate offers an

An efficient async condition variable for lock-free algorithms

async-event An efficient async condition variable for lock-free algorithms, a.k.a. "eventcount". Overview Eventcount-like primitives are useful to mak

A safe sync/async multi-producer, multi-consumer channel
A safe sync/async multi-producer, multi-consumer channel

Loole A safe async/sync multi-producer multi-consumer channel. Producers can send and consumers can receive messages asynchronously or synchronously:

Comments
  • incorrect project name in README

    incorrect project name in README

    To get started working on developing situwatiion [...]

    I guess you copy-pasted the README and forgot to change the project name ^^ situwaition also seems to be spelled wrong btw.

    opened by Wuelle 1
  • Create a default implementation for reset()

    Create a default implementation for reset()

    It's possible to create a default implementation for reset() by iterating through the members of the given struct and setting them all to Default::default().

    Let's do that, and save people having to write their own reset()s.

    enhancement macro 
    opened by t3hmrman 0
Releases(async-dropper-v0.1.3)
Owner
Victor Adossi ("vados")
Most of my stuff is on GitLab :) https://gitlab.com/mrman
Victor Adossi (
Golang like WaitGroup implementation for sync/async Rust.

wg Golang like WaitGroup implementation for sync/async Rust.

Al Liu 8 Dec 6, 2022
Async implementation of the StatusNotifierItem and DbusMenu protocols for building system trays.

System Tray An async implementation of the StatusNotifierItem and DbusMenu protocols for building system trays. Requires Tokio. Example use system_tra

Jake Stanger 3 Mar 29, 2024
Async executor for WebAssembly

There are a number of async task executors available in Rust's ecosystem. However, most (if not all?) of them rely on primitives that might not be available or optimal for WebAssembly deployment at the time.

wasm.rs 65 Dec 31, 2022
The feature-rich, portable async channel library

The feature-rich, portable async channel library > crates.io > docs.rs Why use Postage? Includes a rich set of channels. | barrier | broadcast | dispa

Austin Jones 221 Dec 26, 2022
Mix async code with CPU-heavy thread pools using Tokio + Rayon

tokio-rayon Mix async code with CPU-heavy thread pools using Tokio + Rayon Resources Documentation crates.io TL;DR Sometimes, you're doing async stuff

Andy Barron 74 Jan 2, 2023
Another Async IO Framework based on io_uring

kbio, the Async IO Framework based on io_uring, is used in KuiBaDB to implement async io. Features Support multi-threading concurrent task submission.

KuiBaDB 59 Oct 31, 2022
async-alloc-counter measures max allocations in a future invocation

async-alloc-counter measures max allocations in a future invocation see examples/ for usage This allocator can be used as follows: use async_alloc_cou

Geoffroy Couprie 2 Dec 3, 2021
single file, std only, async Rust executor

whorl - A single file, std only, async Rust executor whorl was created to teach you how async executors work in Rust. It is not the fastest executor n

Michael Gattozzi 459 Dec 29, 2022
BPF library for Async Rust, complementary for libbpf-rs.

libbpf-async A library for writing BPF programs in Async Rust, complementary for libbpf-rs, Rust wrapper for libbpf. Currently, this provides Async-fr

FUJITA Tomonori 13 Nov 9, 2022
Async `TryFrom/TryInto` traits

async-convert Async TryFrom/TryInto traits API Docs | Releases | Contributing Installation $ cargo add async-convert Safety This crate uses #![deny(un

Yosh 4 Mar 4, 2022