use crate::channel::AsyncReceiver;
use crate::container::{ContainerBuilder, WslcContainerHandle};
use crate::error::WslcError;
use crate::image::{ImageInfo, OwnedImageProgress, WslcImageManager};
use crate::session::{SessionBuilder, WslcSessionHandle};
use core::ffi::c_void;
use std::future::Future;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use tokio::sync::{mpsc, oneshot};
use windows_sys::Win32::Foundation::{BOOLEAN, GetLastError, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::System::Threading::{
RegisterWaitForSingleObject, UnregisterWaitEx, WT_EXECUTEONLYONCE,
};
use wslcsdk_sys::types::WslcSignal;
struct EventWaitShared {
sender: Mutex<Option<oneshot::Sender<bool>>>,
wait_handle: std::sync::atomic::AtomicPtr<c_void>,
is_done: AtomicBool,
}
unsafe extern "system" fn win32_wait_callback(context: *mut c_void, timer_or_wait_fired: BOOLEAN) {
if context.is_null() {
return;
}
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let shared = unsafe { Arc::from_raw(context as *const EventWaitShared) };
let is_signaled = timer_or_wait_fired == 0;
shared.is_done.store(true, Ordering::Release);
if let Ok(mut lock) = shared.sender.lock()
&& let Some(tx) = lock.take()
{
let _ = tx.send(is_signaled);
}
}));
}
struct WaitGuard {
shared: Arc<EventWaitShared>,
wait_handle: HANDLE,
raw_ctx: *mut EventWaitShared,
}
impl Drop for WaitGuard {
fn drop(&mut self) {
if let Ok(mut lock) = self.shared.sender.lock() {
let _ = lock.take();
}
if !self.wait_handle.is_null() {
if self.shared.is_done.load(Ordering::Acquire) {
unsafe {
let _ = UnregisterWaitEx(self.wait_handle, INVALID_HANDLE_VALUE);
}
} else {
let success =
unsafe { UnregisterWaitEx(self.wait_handle, std::ptr::null_mut()) };
if success != 0 {
let _ = unsafe { Arc::from_raw(self.raw_ctx) };
}
}
self.wait_handle = std::ptr::null_mut();
}
}
}
fn register_win32_wait(
event: HANDLE,
raw_ctx: *mut EventWaitShared,
timeout_ms: u32,
) -> Result<HANDLE, WslcError> {
let mut wait_handle: HANDLE = std::ptr::null_mut();
let success = unsafe {
RegisterWaitForSingleObject(
&mut wait_handle,
event,
Some(win32_wait_callback),
raw_ctx as *mut c_void,
timeout_ms,
WT_EXECUTEONLYONCE,
)
};
if success == 0 {
let _ = unsafe { Arc::from_raw(raw_ctx) };
let err = unsafe { GetLastError() };
return Err(WslcError::Hresult(
err,
"注册 Win32 线程池等待事件失败".to_string(),
));
}
Ok(wait_handle)
}
pub(crate) async fn wait_win32_event_async(
event: HANDLE,
timeout_ms: u32,
) -> Result<bool, WslcError> {
if event.is_null() {
return Err(WslcError::InvalidHandle);
}
let (tx, rx) = oneshot::channel();
let shared = Arc::new(EventWaitShared {
sender: Mutex::new(Some(tx)),
wait_handle: std::sync::atomic::AtomicPtr::new(std::ptr::null_mut()),
is_done: AtomicBool::new(false),
});
let raw_ctx = Arc::into_raw(shared.clone()) as *mut EventWaitShared;
let wait_handle = register_win32_wait(event, raw_ctx, timeout_ms)?;
shared.wait_handle.store(wait_handle, Ordering::Release);
let guard = WaitGuard {
shared: shared.clone(),
wait_handle,
raw_ctx,
};
let result = rx
.await
.map_err(|_| WslcError::ChannelTerminated("Win32 事件等待通知通道".to_string()))?;
drop(guard);
Ok(result)
}
async fn run_blocking<F, R>(operation: &str, task: F) -> Result<R, WslcError>
where
F: FnOnce() -> Result<R, WslcError> + Send + 'static,
R: Send + 'static,
{
tokio::task::spawn_blocking(task)
.await
.map_err(|e| WslcError::TaskJoin(format!("{operation}失败: {e}")))?
}
impl SessionBuilder {
pub async fn build_async(self) -> Result<WslcSessionHandle, WslcError> {
run_blocking("异步创建会话", move || self.build()).await
}
}
impl ContainerBuilder {
pub async fn build_async(
self,
session: &WslcSessionHandle,
) -> Result<WslcContainerHandle, WslcError> {
let session = session.clone();
run_blocking("异步创建容器", move || self.build(&session)).await
}
}
impl WslcSessionHandle {
pub async fn list_images_async(&self) -> Result<Vec<ImageInfo>, WslcError> {
let session = self.clone();
run_blocking("异步获取镜像列表", move || {
WslcImageManager::list_images(&session)
})
.await
}
pub fn pull_image_with_progress_async(
&self,
uri: String,
registry_auth: Option<String>,
progress_capacity: usize,
) -> (
impl Future<Output = Result<(), WslcError>> + Send + 'static,
AsyncReceiver<OwnedImageProgress>,
) {
let (tx, rx) = mpsc::channel(progress_capacity.max(1));
let dropped_counter = Arc::new(AtomicU64::new(0));
let drop_counter_clone = Arc::clone(&dropped_counter);
let session = self.clone();
let fut = async move {
run_blocking("异步拉取镜像", move || {
WslcImageManager::pull_image(
&session,
&uri,
registry_auth.as_deref(),
Some(move |p: &crate::image::ImageProgress<'_>| {
let owned = p.to_owned();
if tx.try_send(owned).is_err() {
drop_counter_clone.fetch_add(1, Ordering::Relaxed);
}
true
}),
)
})
.await
};
(
fut,
AsyncReceiver::new_with_drop_counter(rx, dropped_counter),
)
}
pub async fn pull_image_async(
&self,
uri: String,
registry_auth: Option<String>,
) -> Result<(), WslcError> {
let (fut, _) = self.pull_image_with_progress_async(uri, registry_auth, 1);
fut.await
}
pub async fn push_image_async(
&self,
image: String,
registry_auth: Option<String>,
) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步推送镜像", move || {
WslcImageManager::push_image(&session, &image, registry_auth.as_deref())
})
.await
}
pub async fn import_image_from_file_async(
&self,
image_name: String,
path: PathBuf,
) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步导入镜像文件", move || {
WslcImageManager::import_image_from_file(&session, &image_name, path)
})
.await
}
pub async fn load_image_from_file_async(&self, path: PathBuf) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步载入镜像文件", move || {
WslcImageManager::load_image_from_file(&session, path)
})
.await
}
pub async fn tag_image_async(
&self,
image: String,
repo: String,
tag: String,
) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步镜像打标签", move || {
WslcImageManager::tag_image(&session, &image, &repo, &tag)
})
.await
}
pub async fn delete_image_async(&self, name_or_id: String) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步删除镜像", move || {
WslcImageManager::delete_image(&session, &name_or_id)
})
.await
}
pub async fn terminate_async(&self) -> Result<(), WslcError> {
let session = self.clone();
run_blocking("异步终止会话", move || session.terminate()).await
}
}
impl WslcContainerHandle {
pub async fn start_async(&self, attach: bool) -> Result<(), WslcError> {
let container = self.clone();
run_blocking("异步启动容器", move || container.start(attach)).await
}
pub async fn stop_async(
&self,
signal: WslcSignal,
timeout_seconds: u32,
) -> Result<(), WslcError> {
let container = self.clone();
run_blocking("异步停止容器", move || {
container.stop(signal, timeout_seconds)
})
.await
}
pub async fn delete_async(&self, force: bool) -> Result<(), WslcError> {
let container = self.clone();
run_blocking("异步删除容器", move || container.delete(force)).await
}
pub async fn inspect_async(&self) -> Result<serde_json::Value, WslcError> {
let container = self.clone();
run_blocking("异步检查容器", move || container.inspect()).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use windows_sys::Win32::System::Threading::{CreateEventW, ResetEvent, SetEvent};
fn current_thread_runtime() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("创建测试用 Tokio 运行时失败")
}
#[test]
fn test_event_wait_timeout_signal_and_cancellation() {
let rt = current_thread_runtime();
rt.block_on(async {
let event = unsafe { CreateEventW(std::ptr::null(), 1, 0, std::ptr::null()) };
assert!(!event.is_null());
let timeout_res = wait_win32_event_async(event, 30).await;
assert!(!timeout_res.expect("等待超时场景不应失败"));
let ev_clone = event as usize;
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
unsafe {
SetEvent(ev_clone as _);
}
});
let signal_res = wait_win32_event_async(event, 1000).await;
assert!(signal_res.expect("等待信号场景不应失败"));
unsafe {
ResetEvent(event);
}
tokio::select! {
_ = wait_win32_event_async(event, 5000) => {
panic!("等待不应在超时前完成");
}
_ = tokio::time::sleep(std::time::Duration::from_millis(20)) => {}
}
unsafe {
windows_sys::Win32::Foundation::CloseHandle(event);
}
});
}
#[test]
fn test_null_event_is_rejected() {
let rt = current_thread_runtime();
rt.block_on(async {
let res = wait_win32_event_async(std::ptr::null_mut(), 10).await;
assert_eq!(res.unwrap_err(), WslcError::InvalidHandle);
});
}
}