1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//! Helpers for constructing Tokio runtimes used by `TestCluster`.
use color_eyre::eyre::Context;
use tokio::runtime::{Builder, Runtime};
use super::panic_utils::nested_runtime_thread_panic;
use crate::error::{BootstrapError, BootstrapResult};
/// Constructs a current-thread Tokio runtime for `TestCluster` lifecycle work.
///
/// Enables all Tokio features (I/O, time, etc.) so embedded `PostgreSQL`
/// operations can run to completion on the dedicated runtime.
///
/// # Errors
///
/// Returns an error when the runtime cannot be built, for example due to
/// resource limits or incompatible platform configuration.
pub(crate) fn build_runtime() -> BootstrapResult<Runtime> {
Builder::new_current_thread()
.enable_all()
.build()
.context("failed to create Tokio runtime for TestCluster")
.map_err(BootstrapError::from)
}
/// Runs lifecycle work with a Tokio runtime, even when already inside one.
///
/// When called from within a Tokio runtime, this helper executes `operation`
/// on a scoped helper thread so runtime creation and teardown stay outside the
/// async context.
pub(crate) fn run_with_runtime<T, F>(context: &'static str, operation: F) -> BootstrapResult<T>
where
F: FnOnce(&Runtime) -> BootstrapResult<T> + Send,
T: Send,
{
if tokio::runtime::Handle::try_current().is_ok() {
return std::thread::scope(|scope| {
scope
.spawn(move || {
let runtime = build_runtime()?;
operation(&runtime)
})
.join()
})
.map_err(|panic_payload| {
nested_runtime_thread_panic(context, "lifecycle", panic_payload)
})?;
}
let runtime = build_runtime()?;
operation(&runtime)
}