use std::os::windows::io::OwnedHandle;
use std::sync::{Arc, Mutex};
use tracing::{error, warn};
use windows::core::Result;
pub use crate::api::{CpuTimes, get_current_thread_id, get_process_times};
pub const ROLE_VBLANK: &str = "vblank";
pub const ROLE_CURSOR_MONITOR: &str = "cursor_monitor";
pub const ROLE_UI: &str = "ui";
pub const ROLE_TICKER_DISPATCHER_KANADE: &str = "ticker_dispatcher_kanade";
pub const ROLE_TICKER_LOOP: &str = "ticker_loop";
pub const ROLE_ACTOR_PREFIX: &str = "actor:";
pub const ROLE_PERF_REPORT: &str = "perf_report";
pub const ROLE_UNREGISTERED_REST: &str = "unregistered_rest";
pub const ALL_ROLES: &[&str] = &[
ROLE_VBLANK,
ROLE_CURSOR_MONITOR,
ROLE_UI,
ROLE_TICKER_DISPATCHER_KANADE,
ROLE_TICKER_LOOP,
ROLE_ACTOR_PREFIX,
ROLE_PERF_REPORT,
ROLE_UNREGISTERED_REST,
];
#[derive(Clone, Debug)]
pub struct ThreadEntry {
pub role: String,
pub name: Option<String>,
pub tid: u32,
pub handle: Arc<OwnedHandle>,
}
impl ThreadEntry {
pub fn cpu_times(&self) -> Result<CpuTimes> {
crate::api::get_thread_times(&self.handle)
}
}
static REGISTRY: Mutex<Vec<ThreadEntry>> = Mutex::new(Vec::new());
fn with_registry<R>(f: impl FnOnce(&mut Vec<ThreadEntry>) -> R) -> R {
let mut guard = REGISTRY
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
f(&mut guard)
}
pub fn role_actor(name: impl AsRef<str>) -> String {
format!("{ROLE_ACTOR_PREFIX}{}", name.as_ref())
}
pub fn is_known_role(role: &str) -> bool {
match role.strip_prefix(ROLE_ACTOR_PREFIX) {
Some(actor_name) => !actor_name.is_empty(),
None => ALL_ROLES.contains(&role),
}
}
pub fn register_current_thread(role: impl Into<String>) -> Result<()> {
let role = role.into();
if !is_known_role(&role) {
warn!(
target: "wintf::thread_registry",
role = %role,
"固定語彙に無い役割名で登録された(報告行には出るが語彙の検査には掛からない)"
);
}
let handle = match crate::api::duplicate_current_thread_handle() {
Ok(handle) => handle,
Err(err) => {
error!(
target: "wintf::thread_registry",
role = %role,
error = %err,
"スレッドハンドルの複製に失敗した——このスレッドは名簿に載らず、CPU は unregistered_rest へ回る"
);
return Err(err);
}
};
let tid = get_current_thread_id();
let name = std::thread::current().name().map(str::to_owned);
let entry = ThreadEntry {
role,
name,
tid,
handle: Arc::new(handle),
};
with_registry(|entries| {
entries.retain(|existing| existing.tid != tid);
entries.push(entry);
});
Ok(())
}
pub fn snapshot() -> Vec<ThreadEntry> {
with_registry(|entries| entries.clone())
}
#[cfg(test)]
#[path = "thread_registry_tests.rs"]
mod tests;