Requesting assistance understanding KeInitializeMutex

All development related issues welcome

Moderator: Moderator Team

Post Reply
Guy1524
Posts: 3
Joined: Wed May 16, 2018 6:11 am

Requesting assistance understanding KeInitializeMutex

Post by Guy1524 »

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
ThFabba
Developer
Posts: 293
Joined: Sun Jul 11, 2010 11:39 am

Re: Requesting assistance understanding KeInitializeMutex

Post by ThFabba »

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).
Guy1524
Posts: 3
Joined: Wed May 16, 2018 6:11 am

Re: Requesting assistance understanding KeInitializeMutex

Post by Guy1524 »

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?
ThFabba
Developer
Posts: 293
Joined: Sun Jul 11, 2010 11:39 am

Re: Requesting assistance understanding KeInitializeMutex

Post by ThFabba »

Kernel code using a mutex will do

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);
}
KeWaitForSingleObject will (https://git.reactos.org/?p=reactos.git; ... c04a2#l465):
  • 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)
So OwnerThread is set/unset directly (without APCs or anything) whenever the mutex is acquired/released.

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
Wine should already have most of the right functionality and behavior in server/mutex.c, it's just not exposed to kernel mode.
Guy1524
Posts: 3
Joined: Wed May 16, 2018 6:11 am

Re: Requesting assistance understanding KeInitializeMutex

Post by Guy1524 »

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:

Code: Select all

InsertTailList(&CurrentObject->Header.WaitListHead,
               &WaitBlock->WaitListEntry);
What exactly is a waitblock?
ThFabba
Developer
Posts: 293
Joined: Sun Jul 11, 2010 11:39 am

Re: Requesting assistance understanding KeInitializeMutex

Post by ThFabba »

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...

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;
... 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).
Post Reply

Who is online

Users browsing this forum: No registered users and 12 guests