# Running code during application shutdown

**URL:** <https://users.rust-lang.org/t/running-code-during-application-shutdown/142041>\
**Category:** help\
**Created:** [August 24, 2026, 4:52pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041 "2026-08-24T16:52:37Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Blackilykat](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/blackilykat/32/53693_2.png) [@Blackilykat](https://users.rust-lang.org/u/Blackilykat)\
**Post date:** [August 24, 2026, 4:52pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041/1 "2026-08-24T16:52:37Z")

</div>

I am currently rewriting a project from java to rust due to memory usage concerns. It is a music player which requires storing some state on disk. The state is auto-saved periodically but should always be saved when the application shuts down.

In the Java world I used to rely heavily on an event system, where it is very easy to store a Runnable for an indefinite amount of time and call it when needed. I have attempted to do something similar to that in rust using Future but am struggling to unbox it in a way that allows me to await it. I've tried combinations of deref, clone, to\_owned, into\_future and more but I couldn't figure it out.

At some point I had something that compiled fine, but it was storing `Fn() -> ()` which is not async and I need to be able to `await` in the handlers I register. Tried with `AsyncFn` but I could not get it to work, so I looked at what tokio::spawn accepts and tried to use Future instead.

Is what I'm trying to do even possible? More importantly: is this just an inappropriate approach to the problem in this language?

My code:

```rust
use std::{sync::LazyLock};

use futures::{FutureExt, future::BoxFuture};
use log::error;
use tokio::{signal, sync::Mutex};

static RAN_ONCE: LazyLock<Mutex<bool>> = LazyLock::new(|| Mutex::new(false));
static HANDLERS: LazyLock<Mutex<Vec<BoxFuture<'static, ()>>>> = LazyLock::new(|| Mutex::new(Vec::new()));

pub async fn register_handler<F>(future: F) where F: Future<Output = ()> + Send + Sync + 'static, {
    HANDLERS.lock().await.push(future.boxed());
}

pub async fn run_handlers() {
    let mut ran_once = RAN_ONCE.lock().await;
    if *ran_once {
        return;
    }
    *ran_once = true;

    drop(ran_once);

    let handlers = HANDLERS.lock().await;

    for i in 0..handlers.len() {
        if let Some(handler) = handlers.get(i) {
            handler.await; // &Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>> is not a future
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![danjl1100](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/danjl1100/32/10835_2.png) [@danjl1100](https://users.rust-lang.org/u/danjl1100)\
**Post date:** [August 24, 2026, 5:21pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041/2 "2026-08-24T17:21:06Z")

</div>

I think you need to await the future by value:

> **[Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=4a63a819a2a6d6076dbcc766bf78b94b)**
>
> A browser interface to the Rust compiler to experiment with the language

This reverses the order, but any method that removes values from the &mut list should work.  
UPDATE: Below look much better 👇

---

<div class="post-metadata">

**Author:** ![farnz](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/farnz/32/14366_2.png) [@farnz](https://users.rust-lang.org/u/farnz)\
**Post date:** [August 24, 2026, 5:21pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041/3 "2026-08-24T17:21:49Z")

</div>

The big thing you're doing that's making your life hard is using indexing, rather than trying to find an iterator over `HANDLERS` that does what you want. As a result, instead of having exclusive access to the save future so that you can poll it, you have shared access and can't change it (which implies you can't `await` it, either, since that changes the internal state of the future).

Does

```rust
use std::{sync::LazyLock};

use futures::{FutureExt, future::BoxFuture};
use log::error;
use tokio::{signal, sync::Mutex};

static HANDLERS: LazyLock<Mutex<Vec<BoxFuture<'static, ()>>>> = LazyLock::new(|| Mutex::new(Vec::new()));

pub async fn register_handler<F>(future: F) where F: Future<Output = ()> + Send + Sync + 'static, {
    HANDLERS.lock().await.push(future.boxed());
}

pub async fn run_handlers() {
    let mut handlers = HANDLERS.lock().await;

    for handler in handlers.drain(..) {
        handler.await;
    }
}

```

work for you? This has two simplifications:

1. We use `drain(..)` to get an iterator over the locked handlers that lets you take ownership of each handler in turn.
2. Because each handler is extracted and run as an owned future, we don't need the `RAN_ONCE` variable - instead, we lean on Rust's type system to guarantee that you get ownership of each future at most once.

There is also an alternative, that I'd prefer, but it's further from your original code:

```rust
use std::sync::{LazyLock, Mutex};

use futures::{FutureExt, future::BoxFuture};

static HANDLERS: LazyLock<Mutex<Vec<BoxFuture<'static, ()>>>> =
    LazyLock::new(|| Mutex::new(Vec::new()));

pub fn register_handler<F>(future: F)
where
    F: Future<Output = ()> + Send + Sync + 'static,
{
    HANDLERS.lock().expect("poison").push(future.boxed());
}

pub async fn run_handlers() {
    let handlers: Vec<_> = std::mem::take(&mut HANDLERS.lock().expect("poison"));

    for handler in handlers {
        handler.await;
    }
}

```

This doesn't use an async lock, and instead removes all of the handlers at once when you call `run_handlers`, and then runs them one at a time. I prefer this, because if you're using a multi-threaded executor (the default Tokio executor, for example), holding a sync lock over a `.await` is a compile-time failure (because `MutexGuard` happens not to be `Send`), and thus it's easier to see that this can't deadlock (because the lock is held for such a short period of time).

---

<div class="post-metadata">

**Author:** ![Blackilykat](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/blackilykat/32/53693_2.png) [@Blackilykat](https://users.rust-lang.org/u/Blackilykat)\
**Post date:** [August 24, 2026, 5:53pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041/4 "2026-08-24T17:53:30Z")

</div>

Thanks! I thought I was making my life easier by not worrying about which function to use, but not having ownership was the problem.

> [@farnz](#):
>
> and thus it's easier to see that this can't deadlock

Do you refer to how a handler may attempt to register another handler? Would this be solvable keeping tokio Mutexes doing something like this?

```rust
pub async fn run_handlers() {
    let mut handlers = HANDLERS.lock().await;

    let mut temp_handlers = Vec::new();
    for handler in handlers.drain(..) {
        temp_handlers.push(handler);
    }

    drop(handlers);

    for handler in temp_handlers.drain(..) {
        handler.await;
    }
}

```

(I am trying to avoid using unwrap or expect in my code, which wouldn't panic here anyway unless something else panicked but better safe than sorry)

---

<div class="post-metadata">

**Author:** ![farnz](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/farnz/32/14366_2.png) [@farnz](https://users.rust-lang.org/u/farnz)\
**Post date:** [August 24, 2026, 6:45pm UTC](https://users.rust-lang.org/t/running-code-during-application-shutdown/142041/5 "2026-08-24T18:45:47Z")

</div>

If you don't care about `panic`s, there's `std::sync::nonpoison::Mutex`, or `parking_lot::Mutex` that you can use.

The problem with Tokio's async `Mutex` is that it can be held over a `.await` - which means that to reason about whether your program contains deadlocks involves reasoning about all places where the `Mutex` is held (and making sure you're not confusing yourself). A sync `Mutex`, like `std::sync::Mutex`, cannot be held across a `.await`, which means that you only have to look at the code from `.lock()` to the first `.await`.
