use crate::com::HandleMtaLease;
use crate::error::WslcError;
use core::ffi::c_void;
use std::os::windows::raw::HANDLE;
use std::sync::Arc;
use windows_sys::Win32::Foundation::{GetLastError, WAIT_FAILED, WAIT_OBJECT_0, WAIT_TIMEOUT};
use windows_sys::Win32::System::Threading::WaitForSingleObject;
use wslcsdk_sys::types::{
WslcCrashDumpSubscription, WslcSession, WslcSessionCrashDumpInfo, WslcSessionTerminationReason,
};
use wslcsdk_sys::*;
#[derive(Debug)]
struct SessionInner {
raw: WslcSession,
name: String,
_mta: HandleMtaLease,
}
impl Drop for SessionInner {
fn drop(&mut self) {
if !self.raw.is_null() {
log::debug!("释放 WSLC 会话句柄,会话名称: '{}'", self.name);
unsafe {
let _ = WslcReleaseSession(self.raw);
}
self.raw = WslcSession::NULL;
}
}
}
unsafe impl Send for SessionInner {}
unsafe impl Sync for SessionInner {}
#[derive(Clone, Debug)]
pub struct WslcSessionHandle {
inner: Arc<SessionInner>,
}
impl WslcSessionHandle {
pub unsafe fn from_raw(raw: WslcSession, name: impl Into<String>) -> Self {
let _mta = HandleMtaLease::acquire().unwrap_or_else(|e| {
log::error!("为外部会话句柄获取 MTA 租约失败,析构时可能崩溃: {e}");
HandleMtaLease::degraded()
});
Self {
inner: Arc::new(SessionInner {
raw,
name: name.into(),
_mta,
}),
}
}
pub unsafe fn try_from_raw(
raw: WslcSession,
name: impl Into<String>,
) -> Result<Self, WslcError> {
let _mta = HandleMtaLease::acquire().map_err(|e| {
WslcError::InvalidConfiguration(format!(
"为外部会话句柄获取 MTA 租约失败,该句柄将不受套间保障: {e}"
))
})?;
Ok(Self {
inner: Arc::new(SessionInner {
raw,
name: name.into(),
_mta,
}),
})
}
pub(crate) fn from_acquired_lease(raw: WslcSession, name: String, mta: HandleMtaLease) -> Self {
Self {
inner: Arc::new(SessionInner {
raw,
name,
_mta: mta,
}),
}
}
pub fn as_raw(&self) -> WslcSession {
self.inner.raw
}
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn termination_event(&self) -> Result<HANDLE, WslcError> {
let mut event: HANDLE = std::ptr::null_mut();
let hr = unsafe { WslcGetSessionTerminationEvent(self.inner.raw, &mut event) };
WslcError::check_hr(hr, "获取会话终止事件句柄失败")?;
Ok(event)
}
pub fn wait_termination(&self, timeout_ms: u32) -> Result<bool, WslcError> {
let event = self.termination_event()?;
let wait_res = unsafe { WaitForSingleObject(event, timeout_ms) };
match wait_res {
WAIT_OBJECT_0 => Ok(true),
WAIT_TIMEOUT => Ok(false),
WAIT_FAILED => {
let err = unsafe { GetLastError() };
Err(WslcError::Hresult(err, "等待会话终止事件失败".to_string()))
}
other => Err(WslcError::Hresult(
other,
format!("等待会话终止返回非预期状态: {other}"),
)),
}
}
pub async fn wait_termination_async(&self, timeout_ms: u32) -> Result<bool, WslcError> {
if self.inner.raw.is_null() {
return Err(WslcError::InvalidHandle);
}
let event = self.termination_event()?;
crate::async_ops::wait_win32_event_async(event, timeout_ms).await
}
pub fn termination_reason(&self) -> Result<WslcSessionTerminationReason, WslcError> {
let mut reason = WslcSessionTerminationReason::Unknown;
let hr = unsafe { WslcGetSessionTerminationReason(self.inner.raw, &mut reason) };
WslcError::check_hr(hr, "获取会话终止原因失败")?;
Ok(reason)
}
pub fn terminate(&self) -> Result<(), WslcError> {
let hr = unsafe { WslcTerminateSession(self.inner.raw) };
WslcError::check_hr(hr, "终止会话失败")?;
log::info!("WSLC 会话已被主动终止,名称: '{}'", self.name());
Ok(())
}
pub fn register_crash_dump_callback<F>(
&self,
callback: F,
) -> Result<CrashDumpSubscription, WslcError>
where
F: Fn(&WslcSessionCrashDumpInfo) + Send + 'static,
{
let boxed_cb = Box::new(callback);
let ctx = Box::into_raw(boxed_cb);
let mut sub = WslcCrashDumpSubscription::NULL;
let mut err_msg: *mut u16 = std::ptr::null_mut();
let hr = unsafe {
WslcRegisterSessionCrashDumpCallback(
self.inner.raw,
Some(crash_trampoline::<F>),
ctx as *mut c_void,
&mut sub,
&mut err_msg,
)
};
if let Err(e) = unsafe { WslcError::check(hr, err_msg) } {
let _ = unsafe { Box::from_raw(ctx) };
return Err(e);
}
Ok(CrashDumpSubscription {
_session: self.clone(),
raw: sub,
ctx: ctx as *mut c_void,
drop_fn: drop_ctx::<F>,
})
}
}
unsafe extern "system" fn crash_trampoline<F>(
info: *const WslcSessionCrashDumpInfo,
context: *mut c_void,
) where
F: Fn(&WslcSessionCrashDumpInfo),
{
if !context.is_null() && !info.is_null() {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let info_ref = unsafe { &*info };
log::warn!(
"检测到会话 Linux 进程崩溃转储,PID: {},信号: {}",
info_ref.pid,
info_ref.signal
);
let cb = unsafe { &*(context as *const F) };
cb(info_ref);
}));
}
}
unsafe fn drop_ctx<F>(context: *mut c_void) {
if !context.is_null() {
let _ = unsafe { Box::from_raw(context as *mut F) };
}
}
pub struct CrashDumpSubscription {
_session: WslcSessionHandle,
raw: WslcCrashDumpSubscription,
ctx: *mut c_void,
drop_fn: unsafe fn(*mut c_void),
}
impl std::fmt::Debug for CrashDumpSubscription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CrashDumpSubscription")
.field("raw", &self.raw)
.field("session", &self._session.name())
.field("has_context", &!self.ctx.is_null())
.finish()
}
}
impl Drop for CrashDumpSubscription {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe {
let _ = WslcReleaseCrashDumpSubscription(self.raw);
}
self.raw = WslcCrashDumpSubscription::NULL;
}
if !self.ctx.is_null() {
unsafe {
(self.drop_fn)(self.ctx);
}
self.ctx = std::ptr::null_mut();
}
}
}
unsafe impl Send for CrashDumpSubscription {}
unsafe impl Sync for CrashDumpSubscription {}