CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。
56 lines
2.1 KiB
Rust
56 lines
2.1 KiB
Rust
//! 同步桥:让**非 async 宿主**驱动异步下载引擎。
|
||
//!
|
||
//! `core::download` 的引擎是异步的(tokio + reqwest),GUI 侧自带运行时;
|
||
//! 但 headless 消费者(`crates/cli` 的 `ull`)不依赖 tokio,也不该为一次安装
|
||
//! 把整个异步运行时依赖树拉进 CLI 的 manifest(D18:CLI 依赖树保持干净)。
|
||
//!
|
||
//! 因此把「建一个短命运行时 + 跑完整段 future」这两件事收在 core 里:
|
||
//!
|
||
//! ```ignore
|
||
//! let outcome = unlucky_core::download::blocking::block_on(async { /* 规划 + 下载 */ });
|
||
//! ```
|
||
//!
|
||
//! 约定:**整段**安装流程包在一次 `block_on` 里(同一个运行时),不要每次下载都建一个——
|
||
//! reqwest 的连接池绑定在创建它的运行时上,跨运行时会退化甚至报「dispatch task is gone」。
|
||
//!
|
||
//! 这是给「单线程 CLI」用的逃生口,不是通用设施:app 层请直接用原生异步。
|
||
|
||
use std::future::Future;
|
||
|
||
/// 在一个临时 tokio 多线程运行时上跑完 `future`。
|
||
///
|
||
/// 运行时随调用结束销毁,因此适合「一条命令一次阻塞」的 CLI 生命周期。
|
||
///
|
||
/// # Panics
|
||
///
|
||
/// 运行时建不起来(系统资源耗尽)时 panic:此时进程已无法继续,早点暴露比静默降级好。
|
||
/// 在已有 tokio 运行时上下文里调用同样会 panic(tokio 不允许嵌套 `block_on`)。
|
||
pub fn block_on<F: Future>(future: F) -> F::Output {
|
||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||
.worker_threads(2)
|
||
.thread_name("ull-blocking")
|
||
.enable_all()
|
||
.build()
|
||
.expect("创建 tokio 运行时失败(内存/线程资源不足)");
|
||
runtime.block_on(future)
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::block_on;
|
||
|
||
#[test]
|
||
fn drives_a_plain_future_to_completion() {
|
||
assert_eq!(block_on(async { 1 + 1 }), 2);
|
||
}
|
||
|
||
#[test]
|
||
fn drives_tokio_timers() {
|
||
let start = std::time::Instant::now();
|
||
block_on(async {
|
||
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
|
||
});
|
||
assert!(start.elapsed() >= std::time::Duration::from_millis(5));
|
||
}
|
||
}
|