I am using ReactOS as a reference in implementing more of wine's ntoskrnl.exe, and I am confused about mutexes / mutants. In the publicly-used KeInitializeMutex function, the mutex's OwnerThread attribute is set to its default value, NULL. This is not the case in the private KeInitializeMutant function. Either way, when KeReleaseMutex/Mutant is called (same thing), it is required that the Mutant's OwnerThread is the current thread, otherwise an exception will be released (STATUS_MUTANT_NOT_OWNED). For non-microsoft drivers, is it expected that the the user manually sets the OwnerThread attribute before using the mutex? Why doesn't KeInitializeMutex do this for the application?
Relevant source file: https://github.com/reactos/reactos/blob ... ke/mutex.c
Requesting assistance understanding KeInitializeMutex
Moderator: Moderator Team
Re: Requesting assistance understanding KeInitializeMutex
OwnerThread refers to the thread that has acquired the mutex. KeInitializeMutex does not acquire the mutex. However KeInitializeMutant does acquire it, if the InitialOwner parameter is specified as TRUE.
So KeInitializeMutex + KeReleaseMutex is supposed to raise an exception, because the mutex is un-owned. You have to call KeWaitForSingleObject (or equivalent) first to acquire the mutex before it is possible to release it. The KeWait functions will do a bunch of stuff, but it boils down to KiSatisfyMutantWait for a mutex: https://git.reactos.org/?p=reactos.git; ... c04a2#l707 (in which you can see that OwnerThread is set).
So KeInitializeMutex + KeReleaseMutex is supposed to raise an exception, because the mutex is un-owned. You have to call KeWaitForSingleObject (or equivalent) first to acquire the mutex before it is possible to release it. The KeWait functions will do a bunch of stuff, but it boils down to KiSatisfyMutantWait for a mutex: https://git.reactos.org/?p=reactos.git; ... c04a2#l707 (in which you can see that OwnerThread is set).
Re: Requesting assistance understanding KeInitializeMutex
Thank you for the explanation!
What confuses me now is how the ownerthread is set to the waiting thread. Is KeReleaseMutex supposed to be queued as an APC for the waiting thread?
What confuses me now is how the ownerthread is set to the waiting thread. Is KeReleaseMutex supposed to be queued as an APC for the waiting thread?
Re: Requesting assistance understanding KeInitializeMutex
Kernel code using a mutex will do
KeWaitForSingleObject will (https://git.reactos.org/?p=reactos.git; ... c04a2#l465):
In general, what's notable about mutexes is:
Code: Select all
KMUTEX Mutex;
VOID
Initialize(VOID)
{
KeInitializeMutex(&Mutex);
}
VOID
DoWork(VOID)
{
(VOID)KeWaitForSingleObject(&Mutex, Executive, KernelMode, FALSE, NULL);
// access data that needs to be protected by the mutex
KeReleaseMutex(&Mutex, FALSE);
}
- If the mutex is not owned (Header.SignalState != 0) or owned by the current thread (recursive acquisition), satisfy the mutex wait: decrease SignalState (unless that would overflow), set the OwnerThread, disable kernel APCs if needed, return success (or STATUS_ABANDONED if appropriate)
- If the mutex is owned by another thread, add the current thread to the mutex's wait list and put it in a wait state (this works the same as for any object with a DISPATCHER_HEADER, e.g. events)
In general, what's notable about mutexes is:
- They can be acquired recursively, so both Wait and Release have to account for that fact (SignalState is decreased with every Wait and increased with every Release; when it goes beyond MINLONG, a STATUS_MUTANT_LIMIT_EXCEEDED exception is raised; when it reaches 0, the mutex is fully released and the next thread in the wait list is woken up)
- They can be abandoned, which will cause a Wait to return the special status STATUS_ABANDONED, and release to raise and exception with that status
- There are mutants and mutexes, which IIRC are basically the same except that the mutex functions are simplified for kernel mode callers, whereas the mutant functions expose the full power of the object and are mostly used to implement the Nt*Mutant syscalls.
- In the code you'll also see some implementation details like keeping track of OwnerThread and keeping acquired mutexes in the thread's MutantList
Re: Requesting assistance understanding KeInitializeMutex
Thank you very much for the response. You say that the current thread is added to the wait list, however, the actual line says this:
What exactly is a waitblock?
Code: Select all
InsertTailList(&CurrentObject->Header.WaitListHead,
&WaitBlock->WaitListEntry);
Re: Requesting assistance understanding KeInitializeMutex
Since a thread can wait on more than one object at the same time (Nt/KeWaitForMultipleObjects), there can't simply be a LIST_ENTRY in the KTHREAD structure for the wait list. Instead, there are four KWAIT_BLOCK's in the KTHREAD structure (one of which is reserved; so when calling KeWaitForMultipleObjects, the caller must supply an array of KWAIT_BLOCK structures when waiting on more than 3 objects).
The KWAIT_BLOCK structure (https://git.reactos.org/?p=reactos.git; ... f3163#l443), which looks like...
... contains the list entry that gets added to DISPATCHER_HEADER::WaitListHead, as well as a pointer to the thread and the object that's being waited for. The KxSingleThreadWait macro called right at the beginning of KeWaitForSingleObject takes care of this (https://git.reactos.org/?p=reactos.git; ... 3163#l1154) -- and there are equivalent macros for KeWaitForMultipleObjects (depending on WaitAny/WaitAll).
The KWAIT_BLOCK structure (https://git.reactos.org/?p=reactos.git; ... f3163#l443), which looks like...
Code: Select all
typedef struct _KWAIT_BLOCK {
LIST_ENTRY WaitListEntry;
struct _KTHREAD *Thread;
PVOID Object;
struct _KWAIT_BLOCK *NextWaitBlock;
USHORT WaitKey;
UCHAR WaitType;
UCHAR SpareByte;
} KWAIT_BLOCK, *PKWAIT_BLOCK, *PRKWAIT_BLOCK;
Who is online
Users browsing this forum: No registered users and 22 guests