Geoff Chappell - Software Analyst
The KMUTEX (formally _KMUTEX) exists as its own structure only in the very first Windows version. As soon as version 3.50, KMUTEX was redefined as another name for the KMUTANT. It has remained so ever since. Yet the early fleeting existence of the KMUTEX as its own structure is not without instructive significance even decades later.
This is because version 3.10 already had the KMUTANT and the unification of the two structures for version 3.50 came with the complication that it was the KMUTEX (or, rather, the functions that operate on the KMUTEX) that had been documented but it was the undocumented KMUTANT that had been chosen to survive.
The KMUTEX (original) and the KMUTANT are both dispatcher objects with the particular property that a thread’s successful wait on the object acquires exclusive owership of the object by the thread. Until this owning thread releases the object, no other thread can acquire the object. An object of either type thus provides for mutually exclusive thread-level access to sections of code that are only entered by acquiring the object and are only left by releasing it.
What distinguished the KMUTEX was the assistance it provided the programmer for tracking the nested acquisition and release of more than one mutex. The danger of deadlock is well known. This occurs when thread T1 owns mutex M1 and thread T2 owns mutex M2 but then T1 waits for M2 and T2 waits for M1. Also well known is a mechanism for avoiding the danger: require that all threads that ever use M1 and M2 must always acquire them in the same order. If in the scenario just presented the order is agreed to be M1 before M2, then only by breaking the rule can T2 be waiting on M1 while owning M2.
Windows NT 3.1 generalises this simple scenario so that every mutex is initialised with an order of acquisition that is named its level. To this day, although the KeInitializeMutex function actually initialises a KMUTANT, its second argument is named Level (but is ignored). In version 3.10, the Level is saved into the initialised KMUTEX and acquisition in increasing order is enforced. It is a serious error—with its own bug check code, MUTEX_LEVEL_NUMBER_VIOLATION—if a thread tries to acquire a mutex that it doesn’t already own but whose level isn’t higher than that of any mutex that the thread does own. If only then, the deadlock of two threads in kernel-mode execution was reason to stop the execution of everything!
The defined levels, if only for KMUTEX structures that are created by the kernel and by Microsoft’s own kernel-mode software, were published by Microsoft in a header named EXLEVELS.H. This starts as early as the Device Driver Kit (DDK) for Windows NT 3.1 but continued even to the DDK for Windows NT 4.0. Incidentally, comments in the header give the date 08-May-1989, confirming that the definition of levels was among the earliest of known programming for Windows NT (slightly more than four years before release).
Not fundamental but nonetheless important enough to rate a mention in the DDK documentation for Windows NT 3.1 is that the KMUTEX is not exposed through the Object Manager. It is for kernel-mode use only. The KMUTANT, by contrast, can be created as a named object and handles can be created for access to it. There thus exists a native API function named NtCreateMutant but no NtCreateMutex. Yet even in version 3.10 Microsoft presented the KMUTANT to Win32 programs not as a mutant object but as a mutex: the Win32 API functions that get a KMUTANT created are CreateMutexA and CreateMutexW.
The KMUTEX is 0x20 bytes. The offsets, names and types of members are known from a C-language definition in the NTDDK.H from the DDK for Windows NT 3.1.
The Header, MutexListEntry (as MutantListEntry) and OwnerThread are reproduced in the KMUTANT.
The Header in the KMUTEX and KMUTANT differ in their Type. As always for the DISPATCHER_HEADER, the Type takes its values from the KOBJECTS enumeration. This is 3 for the KMUTEX. In later versions, this value is ProcessObject and denotes that the DISPATCHER_HEADER instead begins a KPROCESS.
Within a mutex’s DISPATCHER_HEADER the SignalState is 1 (signalled) while the mutex is unowned, as when newly initialised. Acquiring the mutex, including when a thread asks to wait on a mutex that it already owns, decrements the SignalState. Releasing the mutex increments the SignalState. Trying release a mutex that the calling thread does not own causes the bug check THREAD_NOT_MUTEX_OWNER. Releasing the mutex while its SignalState is zero not only returns the mutex to its initial state but also signals the kernel to let a waiting thread acquire the mutex. Put aside some theoretical but remote possibility of wrap-around and the logic of acquiring and releasing does not allow the SignalState to exceed 1.
The Level is set at initialisation. it is checked whenever the kernel tests whether the current thread may acquire the mutex. If indeed the mutex is unowned such that the thread does acquire the mutex, then the (approved) Level is transferred to the KTHREAD as the thread’s MutexLevel. When the thread relinquishes its ownership of the mutex, the MutexLevel is updated from the necessarily lower Level of the mutex at the tail of the thread’s list of owned mutexes, else is cleared to zero.
In version 3.50 and higher, the KMUTEX is re-implemented as an initially unowned KMUTANT whose ApcDisable is 1. Note that this state cannot be achieved with KeInitializeMutant, and so KeInitializeMutex persists. In this sense, the KMUTEX survives as more than an alias of KMUTANT. Kernel-mode programmers still must know whether the structure they allocate is a mutant or a mutex (and user-mode programmers working above NTDLL still work only with mutants, no matter that the nomenclature suggests there’s only the mutex).