Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 19 additions & 18 deletions src/backends/isolation_session/common/src/availability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,37 @@

//! IsolationSession host-availability probe.
//!
//! Availability is whether the in-proc `Windows.AI.IsolationSession`
//! `IsoSessionOps` WinRT class is registered on the OS (its activation factory
//! resolves), matching PR #761 rather than gating on a Windows build number.
//! Available when `IsoSessionOps` activates and reports a feature level above
//! zero for `LocalAgentUser`, which the lifecycle requires. No build number is
//! consulted.

use std::sync::OnceLock;

use isolation_session_bindings::bindings::IsoSessionOps;
use isolation_session_bindings::bindings::{IsoSessionFeature, IsoSessionOps};
use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED};
use windows_core::HRESULT;

static AVAILABLE: OnceLock<bool> = OnceLock::new();

/// Cached for the process; never requires elevation.
pub fn is_isolation_session_available() -> bool {
*AVAILABLE.get_or_init(|| available_from(probe_activation()))
*AVAILABLE.get_or_init(|| available_from(probe_feature_level()))
Comment thread
adpa-ms marked this conversation as resolved.
}

/// Split from [`probe_activation`] so the decision is testable without COM/WinRT.
/// Any activation failure (class not registered, or the OS feature gate off)
/// means not available here.
fn available_from(activation: Result<(), HRESULT>) -> bool {
activation.is_ok()
/// Split from [`probe_feature_level`] so the decision is testable without
/// COM/WinRT. A failed probe or a zero level means not available.
fn available_from(probe: Result<i32, HRESULT>) -> bool {
matches!(probe, Ok(level) if level > 0)
}

fn probe_activation() -> Result<(), HRESULT> {
fn probe_feature_level() -> Result<i32, HRESULT> {
// Guard uninitializes on drop, so a panic in `IsoSessionOps::new()` still
// balances `CoInitializeEx`. The `_ops` handle drops before `_apartment`
// balances `CoInitializeEx`. The `ops` handle drops before `_apartment`
// (reverse declaration order), preserving COM's create-before-uninit rule.
let _apartment = ComApartment::enter();
match IsoSessionOps::new() {
Ok(_ops) => Ok(()),
Err(e) => Err(e.code()),
}
let ops = IsoSessionOps::new().map_err(|e| e.code())?;
ops.GetFeatureLevel(IsoSessionFeature::LocalAgentUser)
.map_err(|e| e.code())
}

/// Owns the COM apartment for the duration of a probe and uninitializes it on
Expand Down Expand Up @@ -71,8 +69,11 @@ mod tests {
use windows::Win32::Foundation::{CLASS_E_CLASSNOTAVAILABLE, REGDB_E_CLASSNOTREG};

#[test]
fn only_successful_activation_means_available() {
assert!(available_from(Ok(())));
fn availability_needs_a_positive_feature_level() {
assert!(available_from(Ok(1)));
assert!(!available_from(Ok(0)));
// Pins the predicate as `> 0` rather than `!= 0`.
assert!(!available_from(Ok(-1)));
assert!(!available_from(Err(CLASS_E_CLASSNOTAVAILABLE)));
assert!(!available_from(Err(REGDB_E_CLASSNOTREG)));
assert!(!available_from(Err(HRESULT(0x8000_4005u32 as i32))));
Expand Down
4 changes: 1 addition & 3 deletions tests/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ node scripts/ci/resolve-validation-test-matrix.mjs --plan nightly

**Skip semantics.** Several suites degrade gracefully on an unsupported host:
the IsolationSession suites decide availability from a single `wxc-exec --probe`
read of `probes.isolationSessionAvailable` (covering both a host that cannot
activate the API and a binary built without `--features isolation_session`),
print `SKIPPED`, and exit 0.
read of `probes.isolationSessionAvailable`, print `SKIPPED`, and exit 0.

Because a skip exits 0 and the dispatchers propagate only the exit code, **a
green CI job does not by itself prove the suite ran.** Anything treating these
Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/run_isolation_session_resize_smoke.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function Get-IsolationSessionProbe {

$probeResult = Get-IsolationSessionProbe -Exe $WxcExec
if ($probeResult.Status -eq 'unavailable') {
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow
exit 0
}
if ($probeResult.Status -ne 'available') {
Expand Down
6 changes: 2 additions & 4 deletions tests/scripts/run_isolation_session_state_aware_tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
PowerShell on that host.

Prerequisite probe (skip if unavailable -- not a failure):
- `wxc-exec --probe` reports `probes.isolationSessionAvailable`. This
single signal covers an OS that cannot activate the isolation session
API as well as a binary built without `--features isolation_session`.
- `wxc-exec --probe` reports `probes.isolationSessionAvailable`.

.PARAMETER WxcExePath
Path to wxc-exec.exe. Default probes the host-arch target dir, then
Expand Down Expand Up @@ -148,7 +146,7 @@ function Get-IsolationSessionProbe {

$probeResult = Get-IsolationSessionProbe -Exe $WxcExec
if ($probeResult.Status -eq 'unavailable') {
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow
exit 0
}
if ($probeResult.Status -ne 'available') {
Expand Down
9 changes: 2 additions & 7 deletions tests/scripts/run_isolation_session_tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,7 @@ Write-Host "Configs: $ConfigDir`n" -ForegroundColor Gray
# ---------------- Backend-availability probe ----------------
#
# Availability is decided by a single call to `wxc-exec --probe`, which reports
# `probes.isolationSessionAvailable`. That one signal covers both reasons this
# suite can be unrunnable:
#
# - the host cannot activate the isolation session API, or
# - wxc-exec was built without `--features isolation_session`, in which case
# the field stays false because the backend is not compiled in.
# `probes.isolationSessionAvailable`.
#
# This deliberately replaces the earlier checks for a specific DLL and a
# hard-coded WinRT activatable-class registry key. Those named a runtime class
Expand Down Expand Up @@ -199,7 +194,7 @@ function Get-IsolationSessionProbe {

$probeResult = Get-IsolationSessionProbe -Exe $WxcExec
if ($probeResult.Status -eq 'unavailable') {
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow
Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow
exit 0
}
if ($probeResult.Status -ne 'available') {
Expand Down
Loading