/*
- * ReactOS kernel
- * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id: mutex.c,v 1.11 2002/09/07 15:12:57 chorns Exp $
- *
+ * COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ke/mutex.c
- * PURPOSE: Implements mutex
- * PROGRAMMER: David Welch (welch@mcmail.com)
- * UPDATE HISTORY:
- * Created 22/05/98
+ * PURPOSE: Implements Mutexes and Mutants (that silly davec...)
+ *
+ * PROGRAMMERS:
+ * Alex Ionescu (alex@relsoft.net) - Reorganized/commented some of the code.
+ * Simplified some functions, fixed some return values and
+ * corrected some minor bugs, added debug output.
+ * David Welch (welch@mcmail.com)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
-
#define NDEBUG
#include <internal/debug.h>
-
/* FUNCTIONS *****************************************************************/
-VOID STDCALL
-KeInitializeMutex(IN PKMUTEX Mutex,
- IN ULONG Level)
+/*
+ * @implemented
+ */
+VOID
+STDCALL
+KeInitializeMutant(IN PKMUTANT Mutant,
+ IN BOOLEAN InitialOwner)
{
- KeInitializeDispatcherHeader(&Mutex->Header,
- InternalMutexType,
- sizeof(KMUTEX) / sizeof(ULONG),
- 1);
- Mutex->MutantListEntry.Flink = NULL;
- Mutex->MutantListEntry.Blink = NULL;
- Mutex->OwnerThread = NULL;
- Mutex->Abandoned = FALSE;
- Mutex->ApcDisable = 1;
+ ULONG Signaled = TRUE;
+ PKTHREAD CurrentThread = NULL;
+ KIRQL OldIrql;
+
+ DPRINT("KeInitializeMutant: %x\n", Mutant);
+
+ /* Check if we have an initial owner */
+ if (InitialOwner == TRUE) {
+
+ /* In this case, the object is not signaled */
+ Signaled = FALSE;
+
+ /* We also need to associate a thread */
+ CurrentThread = KeGetCurrentThread();
+
+ /* We're about to touch the Thread, so lock the Dispatcher */
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* And insert it into its list */
+ InsertTailList(&CurrentThread->MutantListHead, &Mutant->MutantListEntry);
+
+ /* Release Dispatcher Lock */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
+ DPRINT("Mutant with Initial Owner\n");
+
+ } else {
+
+ /* In this case, we don't have an owner yet */
+ Mutant->OwnerThread = NULL;
+ }
+
+ /* Now we set up the Dispatcher Header */
+ KeInitializeDispatcherHeader(&Mutant->Header,
+ MutantObject,
+ sizeof(KMUTANT) / sizeof(ULONG),
+ Signaled);
+
+ /* Initialize the default data */
+ Mutant->OwnerThread = CurrentThread;
+ Mutant->Abandoned = FALSE;
+ Mutant->ApcDisable = 0;
}
-LONG STDCALL
-KeReadStateMutex(IN PKMUTEX Mutex)
+/*
+ * @implemented
+ */
+VOID
+STDCALL
+KeInitializeMutex(IN PKMUTEX Mutex,
+ IN ULONG Level)
{
- return(Mutex->Header.SignalState);
+ DPRINT("KeInitializeMutex: %x\n", Mutex);
+
+
+ /* Set up the Dispatcher Header */
+ KeInitializeDispatcherHeader(&Mutex->Header,
+ MutantObject,
+ sizeof(KMUTEX) / sizeof(ULONG),
+ 1);
+
+ /* Initialize the default data */
+ Mutex->OwnerThread = NULL;
+ Mutex->Abandoned = FALSE;
+ Mutex->ApcDisable = 1;
+ InitializeListHead(&Mutex->Header.WaitListHead);
}
-LONG STDCALL
-KeReleaseMutex(IN PKMUTEX Mutex,
- IN BOOLEAN Wait)
+/*
+ * @implemented
+ */
+LONG
+STDCALL
+KeReadStateMutant(IN PKMUTANT Mutant)
{
- KeAcquireDispatcherDatabaseLock(Wait);
- if (Mutex->OwnerThread != KeGetCurrentThread())
- {
- DbgPrint("THREAD_NOT_MUTEX_OWNER: Mutex %p\n", Mutex);
- KeBugCheck(0); /* THREAD_NOT_MUTEX_OWNER */
- }
- Mutex->Header.SignalState++;
- assert(Mutex->Header.SignalState <= 1);
- if (Mutex->Header.SignalState == 1)
- {
- Mutex->OwnerThread = NULL;
- if (Mutex->MutantListEntry.Flink && Mutex->MutantListEntry.Blink)
- RemoveEntryList(&Mutex->MutantListEntry);
- KeDispatcherObjectWake(&Mutex->Header);
- }
- KeReleaseDispatcherDatabaseLock(Wait);
- return(0);
+ /* Return the Signal State */
+ return(Mutant->Header.SignalState);
}
-NTSTATUS STDCALL
-KeWaitForMutexObject(IN PKMUTEX Mutex,
- IN KWAIT_REASON WaitReason,
- IN KPROCESSOR_MODE WaitMode,
- IN BOOLEAN Alertable,
- IN PLARGE_INTEGER Timeout)
+/*
+ * @implemented
+ */
+LONG
+STDCALL
+KeReadStateMutex(IN PKMUTEX Mutex)
{
- return(KeWaitForSingleObject(Mutex,WaitReason,WaitMode,Alertable,Timeout));
+ /* Return the Signal State */
+ return(Mutex->Header.SignalState);
}
-
-VOID STDCALL
-KeInitializeMutant(IN PKMUTANT Mutant,
- IN BOOLEAN InitialOwner)
+/*
+ * @implemented
+ */
+LONG
+STDCALL
+KeReleaseMutant(IN PKMUTANT Mutant,
+ IN KPRIORITY Increment,
+ IN BOOLEAN Abandon,
+ IN BOOLEAN Wait)
{
- if (InitialOwner == TRUE)
- {
- KeInitializeDispatcherHeader(&Mutant->Header,
- InternalMutexType,
- sizeof(KMUTANT) / sizeof(ULONG),
- 0);
- InsertTailList(&KeGetCurrentThread()->MutantListHead,
- &Mutant->MutantListEntry);
- Mutant->OwnerThread = KeGetCurrentThread();
+ KIRQL OldIrql;
+ LONG PreviousState;
+ PKTHREAD CurrentThread = KeGetCurrentThread();
+
+ DPRINT("KeReleaseMutant: %x\n", Mutant);
+
+ /* Lock the Dispatcher Database */
+ OldIrql = KeAcquireDispatcherDatabaseLock();
+
+ /* Save the Previous State */
+ PreviousState = Mutant->Header.SignalState;
+
+ /* Check if it is to be abandonned */
+ if (Abandon == FALSE) {
+
+ /* Make sure that the Owner Thread is the current Thread */
+ if (Mutant->OwnerThread != CurrentThread) {
+
+ KeReleaseDispatcherDatabaseLock(OldIrql);
+
+ DPRINT1("Trying to touch a Mutant that the caller doesn't own!\n");
+ ExRaiseStatus(STATUS_MUTANT_NOT_OWNED);
+ }
+
+ /* If the thread owns it, then increase the signal state */
+ Mutant->Header.SignalState++;
+
+ } else {
+
+ /* It's going to be abandonned */
+ DPRINT("Abandonning the Mutant\n");
+ Mutant->Header.SignalState = 1;
+ Mutant->Abandoned = TRUE;
}
- else
- {
- KeInitializeDispatcherHeader(&Mutant->Header,
- InternalMutexType,
- sizeof(KMUTANT) / sizeof(ULONG),
- 1);
- Mutant->MutantListEntry.Flink = NULL;
- Mutant->MutantListEntry.Blink = NULL;
- Mutant->OwnerThread = NULL;
+
+ /* Check if the signal state is only single */
+ if (Mutant->Header.SignalState == 1) {
+
+ /* Check if it's below 0 now */
+ if (PreviousState <= 0) {
+
+ /* Remove the mutant from the list */
+ DPRINT("Removing Mutant\n");
+ RemoveEntryList(&Mutant->MutantListEntry);
+
+ /* Reenable APCs */
+ DPRINT("Re-enabling APCs\n");
+ CurrentThread->KernelApcDisable += Mutant->ApcDisable;
+
+ /* Force an Interrupt if Apcs are pending */
+ if (!IsListEmpty(&CurrentThread->ApcState.ApcListHead[KernelMode])) {
+
+ /* Make sure they aren't disabled though */
+ if (!CurrentThread->KernelApcDisable) {
+
+ /* Request the Interrupt */
+ DPRINT("Requesting APC Interupt\n");
+ HalRequestSoftwareInterrupt(APC_LEVEL);
+ }
+ }
+ }
+
+ /* Remove the Owning Thread and wake it */
+ Mutant->OwnerThread = NULL;
+
+ /* Check if the Wait List isn't empty */
+ DPRINT("Checking whether to wake the Mutant\n");
+ if (!IsListEmpty(&Mutant->Header.WaitListHead)) {
+
+ /* Wake the Mutant */
+ DPRINT("Waking the Mutant\n");
+ KiWaitTest(&Mutant->Header, Increment);
+ }
}
- Mutant->Abandoned = FALSE;
- Mutant->ApcDisable = 0;
-}
-LONG STDCALL
-KeReadStateMutant(IN PKMUTANT Mutant)
-{
- return(Mutant->Header.SignalState);
+ /* If the Wait is true, then return with a Wait and don't unlock the Dispatcher Database */
+ if (Wait == FALSE) {
+
+ /* Release the Lock */
+ KeReleaseDispatcherDatabaseLock(OldIrql);
+
+ } else {
+
+ /* Set a wait */
+ CurrentThread->WaitNext = TRUE;
+ CurrentThread->WaitIrql = OldIrql;
+ }
+
+ /* Return the previous state */
+ return PreviousState;
}
-LONG STDCALL
-KeReleaseMutant(IN PKMUTANT Mutant,
- IN KPRIORITY Increment,
- IN BOOLEAN Abandon,
- IN BOOLEAN Wait)
+/*
+ * @implemented
+ */
+LONG
+STDCALL
+KeReleaseMutex(IN PKMUTEX Mutex,
+ IN BOOLEAN Wait)
{
- KeAcquireDispatcherDatabaseLock(Wait);
- if (Abandon == FALSE)
- {
- if (Mutant->OwnerThread != NULL && Mutant->OwnerThread != KeGetCurrentThread())
- {
- DbgPrint("THREAD_NOT_MUTEX_OWNER: Mutant->OwnerThread %p CurrentThread %p\n",
- Mutant->OwnerThread,
- KeGetCurrentThread());
- KeBugCheck(0); /* THREAD_NOT_MUTEX_OWNER */
- }
- Mutant->Header.SignalState++;
- assert(Mutant->Header.SignalState <= 1);
- }
- else
- {
- if (Mutant->OwnerThread != NULL)
- {
- Mutant->Header.SignalState = 1;
- Mutant->Abandoned = TRUE;
- }
- }
- if (Mutant->Header.SignalState == 1)
- {
- Mutant->OwnerThread = NULL;
- if (Mutant->MutantListEntry.Flink && Mutant->MutantListEntry.Blink)
- RemoveEntryList(&Mutant->MutantListEntry);
- KeDispatcherObjectWake(&Mutant->Header);
- }
+ /* There's no difference at this level between the two */
+ return KeReleaseMutant(Mutex, IO_NO_INCREMENT, FALSE, Wait);
+}
- KeReleaseDispatcherDatabaseLock(Wait);
- return(0);
+/*
+ * @implemented
+ */
+NTSTATUS
+STDCALL
+KeWaitForMutexObject(IN PKMUTEX Mutex,
+ IN KWAIT_REASON WaitReason,
+ IN KPROCESSOR_MODE WaitMode,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout)
+{
+ /* This is a simple macro. Export the function here though */
+ return KeWaitForSingleObject(Mutex,
+ WaitReason,
+ WaitMode,
+ Alertable,
+ Timeout);
}
/* EOF */