pub struct KMutex<T> { /* private fields */ }
Expand description
A thread safe mutex implemented through acquiring a KMUTEX in the Windows kernel.
The type Kmutex<T>
provides mutually exclusive access to the inner type T allocated through
this crate in the non-paged pool. All data required to initialise the KMutex is allocated in the
non-paged pool and as such is safe to pass stack data into the type as it will not go out of scope.
KMutex
holds an inner value which is a pointer to a KMutexInner
type which is the actual type
allocated in the non-paged pool, and this holds information relating to the mutex.
Access to the T
within the KMutex
can be done through calling Self::lock
.
§Lifetimes
As the KMutex
is designed to be used in the Windows Kernel, with the Windows wdk
crate, the lifetimes of
the KMutex
must be considered by the caller. See examples below for usage.
The KMutex
can exist in a locally scoped function with little additional configuration. To use the mutex across
thread boundaries, or to use it in callback functions, you can use the Grt
module found in this crate. See below for
details.
§Deallocation
KMutex handles the deallocation of resources at the point the KMutex is dropped.
§Examples
§Locally scoped mutex:
{
let mtx = KMutex::new(0u32).unwrap();
let lock = mtx.lock().unwrap();
// If T implements display, you do not need to dereference the lock to print.
println!("The value is: {}", lock);
} // Mutex will become unlocked as it is managed via RAII
§Global scope via the Grt
module in wdk-mutex
:
// Initialise the mutex on DriverEntry
#[export_name = "DriverEntry"]
pub unsafe extern "system" fn driver_entry(
driver: &mut DRIVER_OBJECT,
registry_path: PCUNICODE_STRING,
) -> NTSTATUS {
if let Err(e) = Grt::init() {
println!("Error creating Grt!: {:?}", e);
return STATUS_UNSUCCESSFUL;
}
// ...
my_function();
}
// Register a new Mutex in the `Grt` of value 0u32:
pub fn my_function() {
Grt::register_kmutex("my_test_mutex", 0u32);
}
unsafe extern "C" fn my_thread_fn_pointer(_: *mut c_void) {
let my_mutex = Grt::get_kmutex::<u32>("my_test_mutex");
if let Err(e) = my_mutex {
println!("Error in thread: {:?}", e);
return;
}
let mut lock = my_mutex.unwrap().lock().unwrap();
*lock += 1;
}
// Destroy the Grt to prevent memory leak on DriverExit
extern "C" fn driver_exit(driver: *mut DRIVER_OBJECT) {
unsafe {Grt::destroy()};
}
Implementations§
Source§impl<T> KMutex<T>
impl<T> KMutex<T>
Sourcepub fn new(data: T) -> Result<Self, DriverMutexError>
pub fn new(data: T) -> Result<Self, DriverMutexError>
Sourcepub fn lock(&self) -> Result<KMutexGuard<'_, T>, DriverMutexError>
pub fn lock(&self) -> Result<KMutexGuard<'_, T>, DriverMutexError>
Acquires a mutex in a non-alertable manner.
Once the thread has acquired the mutex, it will return a KMutexGuard
which is a RAII scoped
guard allowing exclusive access to the inner T.
§Errors
If the IRQL is too high, this function will return an error and will not acquire a lock. To prevent a kernel panic, the caller should match the return value rather than just unwrapping the value.
§IRQL
This function must be called at IRQL <= APC_LEVEL
, if the IRQL is higher than this,
the function will return an error.
It is the callers responsibility to ensure the IRQL is sufficient to call this function and it will not alter the IRQL for the caller, as this may introduce undefined behaviour elsewhere in the driver / kernel.
§Examples
let mtx = KMutex::new(0u32).unwrap();
let lock = mtx.lock().unwrap();
Sourcepub unsafe fn to_owned(self) -> T
pub unsafe fn to_owned(self) -> T
Consumes the mutex and returns an owned copy of the protected data (T
).
This method performs a deep copy of the data (T
) guarded by the mutex before
deallocating the internal memory. Be cautious when using this method with large
data types, as it may lead to inefficiencies or stack overflows.
For scenarios involving large data that you prefer not to allocate on the stack,
consider using Self::to_owned_box
instead.
§Safety
- Single Ownership Guarantee: After calling
Self::to_owned
, ensure that no other references (especially static or global ones) attempt to access the underlying mutex. This is because the mutexes memory is deallocated once this method is invoked. - Exclusive Access: This function should only be called when you can guarantee
that there will be no further access to the protected
T
. Violating this can lead to undefined behavior since the memory is freed after the call.
§Example
unsafe {
let owned_data: T = mutex.to_owned();
// Use `owned_data` safely here
}
Sourcepub unsafe fn to_owned_box(self) -> Box<T>
pub unsafe fn to_owned_box(self) -> Box<T>
Consumes the mutex and returns an owned Box<T>
containing the protected data (T
).
This method is an alternative to Self::to_owned
and is particularly useful when
dealing with large data types. By returning a Box<T>
, the data is pool-allocated,
avoiding potential stack overflows associated with large stack allocations.
§Safety
- Single Ownership Guarantee: After calling
Self::to_owned_box
, ensure that no other references (especially static or global ones) attempt to access the underlying mutex. This is because the mutexes memory is deallocated once this method is invoked. - Exclusive Access: This function should only be called when you can guarantee
that there will be no further access to the protected
T
. Violating this can lead to undefined behavior since the memory is freed after the call.
§Example
unsafe {
let boxed_data: Box<T> = mutex.to_owned_box();
// Use `boxed_data` safely here
}