use crate::{Error, Handle, Process, WinResult};
use std::{
mem,
ops::Deref,
os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
};
use winapi::{
shared::{
basetsd::{DWORD_PTR, ULONG64},
minwindef::DWORD,
},
um::{
processthreadsapi::{
GetCurrentThread,
GetThreadId,
GetThreadIdealProcessorEx,
GetThreadPriority,
OpenThread,
ResumeThread,
SetThreadIdealProcessor,
SetThreadPriority,
SuspendThread,
TerminateThread,
},
realtimeapiset::QueryThreadCycleTime,
tlhelp32::{Thread32Next, THREADENTRY32},
winbase::{
SetThreadAffinityMask,
THREAD_MODE_BACKGROUND_BEGIN,
THREAD_MODE_BACKGROUND_END,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_HIGHEST,
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_TIME_CRITICAL,
},
winnt::{self, PROCESSOR_NUMBER, THREAD_ALL_ACCESS},
},
};
#[derive(Debug)]
pub struct Thread {
handle: Handle,
}
impl Thread {
pub fn from_id(id: u32) -> WinResult<Thread> {
unsafe {
let handle = OpenThread(THREAD_ALL_ACCESS, 0, id);
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(Thread {
handle: Handle::new(handle),
})
}
}
}
pub fn current() -> Thread {
unsafe {
Thread {
handle: Handle::from_raw_handle(GetCurrentThread() as RawHandle),
}
}
}
pub fn handle(&self) -> &Handle {
&self.handle
}
pub fn id(&self) -> u32 {
unsafe { GetThreadId(self.handle.as_raw_handle() as winnt::HANDLE) }
}
pub fn cycle_time(&self) -> WinResult<u64> {
unsafe {
let mut cycles: ULONG64 = 0;
let ret =
QueryThreadCycleTime(self.handle.as_raw_handle() as winnt::HANDLE, &mut cycles);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(cycles as u64)
}
}
}
pub fn priority(&self) -> WinResult<PriorityLevel> {
unsafe {
let ret = GetThreadPriority(self.handle.as_raw_handle() as winnt::HANDLE);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(PriorityLevel::from_code(ret as _))
}
}
}
pub fn set_priority(&mut self, priority: PriorityLevel) -> WinResult {
unsafe {
let ret = SetThreadPriority(
self.handle.as_raw_handle() as winnt::HANDLE,
priority.as_code() as _,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn start_background_mode(&mut self) -> WinResult {
unsafe {
let ret = SetThreadPriority(
self.handle.as_raw_handle() as winnt::HANDLE,
THREAD_MODE_BACKGROUND_BEGIN as _,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn end_background_mode(&mut self) -> WinResult {
unsafe {
let ret = SetThreadPriority(
self.handle.as_raw_handle() as winnt::HANDLE,
THREAD_MODE_BACKGROUND_END as _,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn suspend(&mut self) -> WinResult<u32> {
unsafe {
let ret = SuspendThread(self.handle.as_raw_handle() as winnt::HANDLE);
if ret == u32::max_value() {
Err(Error::last_os_error())
} else {
Ok(ret)
}
}
}
pub fn resume(&mut self) -> WinResult<u32> {
unsafe {
let ret = ResumeThread(self.handle.as_raw_handle() as winnt::HANDLE);
if ret == u32::max_value() {
Err(Error::last_os_error())
} else {
Ok(ret)
}
}
}
pub fn terminate(&mut self, exit_code: u32) -> WinResult {
unsafe {
let ret = TerminateThread(self.handle.as_raw_handle() as winnt::HANDLE, exit_code);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn ideal_processor(&self) -> WinResult<u32> {
unsafe {
let mut ideal: PROCESSOR_NUMBER = mem::zeroed();
let ret =
GetThreadIdealProcessorEx(self.handle.as_raw_handle() as winnt::HANDLE, &mut ideal);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(ideal.Number as u32)
}
}
}
pub fn set_ideal_processor(&mut self, processor: u32) -> WinResult<u32> {
unsafe {
let ret = SetThreadIdealProcessor(
self.handle.as_raw_handle() as winnt::HANDLE,
processor as DWORD,
);
if ret == DWORD::max_value() {
Err(Error::last_os_error())
} else {
Ok(ret)
}
}
}
pub fn affinity_mask(&self) -> WinResult<usize> {
unsafe {
let affinity = SetThreadAffinityMask(
self.handle.as_raw_handle() as winnt::HANDLE,
DWORD_PTR::max_value(),
);
if affinity == 0 {
Err(Error::last_os_error())
} else {
let ret =
SetThreadAffinityMask(self.handle.as_raw_handle() as winnt::HANDLE, affinity);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(affinity)
}
}
}
}
pub fn set_affinity_mask(&mut self, mask: usize) -> WinResult<usize> {
unsafe {
let ret = SetThreadAffinityMask(
self.handle.as_raw_handle() as winnt::HANDLE,
mask as DWORD_PTR,
);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(ret)
}
}
}
pub fn set_affinity(&mut self, processor: u8) -> WinResult<usize> {
let processor = processor as usize;
if processor >= mem::size_of::<usize>() * 8 {
self.affinity_mask()
} else {
self.set_affinity_mask(1 << processor)
}
}
}
impl AsRawHandle for Thread {
fn as_raw_handle(&self) -> RawHandle {
self.handle.as_raw_handle()
}
}
impl Deref for Thread {
type Target = winnt::HANDLE;
fn deref(&self) -> &winnt::HANDLE {
&*self.handle
}
}
impl FromRawHandle for Thread {
unsafe fn from_raw_handle(handle: RawHandle) -> Thread {
Thread {
handle: Handle::new(handle as winnt::HANDLE),
}
}
}
impl IntoRawHandle for Thread {
fn into_raw_handle(self) -> RawHandle {
self.handle.into_raw_handle()
}
}
#[derive(Debug)]
pub struct ThreadIter<'a> {
pub(crate) process: &'a Process,
pub(crate) snapshot: Handle,
}
impl<'a> Iterator for ThreadIter<'a> {
type Item = WinResult<Thread>;
fn next(&mut self) -> Option<WinResult<Thread>> {
unsafe {
loop {
let mut entry: THREADENTRY32 = mem::zeroed();
entry.dwSize = mem::size_of::<THREADENTRY32>() as DWORD;
let ret = Thread32Next(self.snapshot.as_raw_handle() as winnt::HANDLE, &mut entry);
if ret == 0 {
return None;
} else {
if entry.th32OwnerProcessID == self.process.id() {
return Some(Thread::from_id(entry.th32ThreadID));
}
}
}
}
}
}
#[derive(Debug)]
pub struct ThreadIdIter<'a> {
pub(crate) process: &'a Process,
pub(crate) snapshot: Handle,
}
impl<'a> Iterator for ThreadIdIter<'a> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
unsafe {
loop {
let mut entry: THREADENTRY32 = mem::zeroed();
entry.dwSize = mem::size_of::<THREADENTRY32>() as DWORD;
let ret = Thread32Next(self.snapshot.as_raw_handle() as winnt::HANDLE, &mut entry);
if ret == 0 {
return None;
} else {
if entry.th32OwnerProcessID == self.process.id() {
return Some(entry.th32ThreadID);
}
}
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum PriorityLevel {
Idle,
Lowest,
BelowNormal,
Normal,
AboveNormal,
Highest,
TimeCritical,
}
impl PriorityLevel {
fn from_code(code: DWORD) -> PriorityLevel {
match code {
THREAD_PRIORITY_IDLE => PriorityLevel::Idle,
THREAD_PRIORITY_LOWEST => PriorityLevel::Lowest,
THREAD_PRIORITY_BELOW_NORMAL => PriorityLevel::BelowNormal,
THREAD_PRIORITY_NORMAL => PriorityLevel::Normal,
THREAD_PRIORITY_ABOVE_NORMAL => PriorityLevel::AboveNormal,
THREAD_PRIORITY_HIGHEST => PriorityLevel::Highest,
THREAD_PRIORITY_TIME_CRITICAL => PriorityLevel::TimeCritical,
_ => panic!("Unexpected priority code: {}", code),
}
}
fn as_code(&self) -> DWORD {
match self {
PriorityLevel::Idle => THREAD_PRIORITY_IDLE,
PriorityLevel::Lowest => THREAD_PRIORITY_LOWEST,
PriorityLevel::BelowNormal => THREAD_PRIORITY_BELOW_NORMAL,
PriorityLevel::Normal => THREAD_PRIORITY_NORMAL,
PriorityLevel::AboveNormal => THREAD_PRIORITY_ABOVE_NORMAL,
PriorityLevel::Highest => THREAD_PRIORITY_HIGHEST,
PriorityLevel::TimeCritical => THREAD_PRIORITY_TIME_CRITICAL,
}
}
}
impl Default for PriorityLevel {
fn default() -> PriorityLevel {
PriorityLevel::Normal
}
}