Crates.io | nolocal-block-on |
lib.rs | nolocal-block-on |
version | 1.0.1 |
source | src |
created_at | 2024-08-05 07:18:24.400143 |
updated_at | 2024-08-05 07:34:53.471618 |
description | futures_lite::future::block_on that can run without using thread-locals |
homepage | https://gitlab.com/infomorphic-matti/nolocal-block-on |
repository | https://gitlab.com/infomorphic-matti/nolocal-block-on |
max_upload_size | |
id | 1325596 |
size | 25,807 |
Version of futures_lite::future::block_on
that does not use any thread locals internally. This is a very specialized crate designed for use in special parts of a program (such as in libc::atexit
callbacks, or things occurring pre- or post- main) where thread local storage will not function.
If your future itself uses thread-local storage, though, then you're out of luck (or need to change the implementation of the futures you're using).
You almost certainly want the normal futures_lite::future::block_on
except in specific circumstances as described above.
The way you invoke the function provided by this crate is the same as the futures-lite
version:
let val = nolocal_block_on::block_on(async {
1 + 2
});
assert_eq!(val, 3);
However, the futures-lite
version does not work in (rare) places where thread-local storage cannot function - for example, running after the main rust runtime is getting shut down. To illustrate this, we demonstrate it via libc::atexit
.
The following code, using futures_lite::future::block_on
, might panic due to using thread-locals in the implementation - and in particular, when multiple threads are involved (such as with blocking::Unblock
), it will panic consistently:
use futures_lite::{future::block_on, io::AsyncWriteExt};
use async_lock::Mutex;
use std::{sync::LazyLock, io::{Stdout, self}};
use blocking::Unblock;
static STDOUT: LazyLock<Mutex<Unblock<Stdout>>> = LazyLock::new(|| {
Mutex::new(Unblock::new(io::stdout()))
});
block_on(async { STDOUT.lock().await.write(b"hello program\n").await });
extern "C" fn cleanup() {
block_on(async {
let mut locked_stdout = STDOUT.lock().await;
locked_stdout.write(b"goodbye program!\n").await;
locked_stdout.flush().await;
});
}
unsafe { libc::atexit(cleanup); }
However, the following code using nolocal_block_on::block_on
, will not panic, as the implementation doesn't use any thread-locals (for non-trivial futures, you might need to check to make sure they don't use thread-locals in their implementations):
use nolocal_block_on::block_on;
use futures_lite::io::AsyncWriteExt;
use async_lock::Mutex;
use std::{sync::LazyLock, io::{Stdout, self}};
use blocking::Unblock;
static STDOUT: LazyLock<Mutex<Unblock<Stdout>>> = LazyLock::new(|| {
Mutex::new(Unblock::new(io::stdout()))
});
block_on(async { STDOUT.lock().await.write(b"hello program\n").await });
extern "C" fn cleanup() {
block_on(async {
let mut locked_stdout = STDOUT.lock().await;
locked_stdout.write(b"goodbye program!\n").await;
locked_stdout.flush().await;
});
}
unsafe { libc::atexit(cleanup); }