KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / mutex.c
index 8190a6c..2ff0507 100644 (file)
@@ -1,34 +1,20 @@
 /*
- *  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.17 2004/08/15 16:39:05 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 *****************************************************************/
 /*
  * @implemented
  */
-VOID STDCALL
-KeInitializeMutex(IN PKMUTEX Mutex,
-                 IN ULONG Level)
+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;
 }
 
 /*
  * @implemented
  */
-LONG STDCALL
-KeReadStateMutex(IN PKMUTEX Mutex)
+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);
 }
 
 /*
  * @implemented
  */
-LONG STDCALL
-KeReleaseMutex(IN PKMUTEX Mutex,
-              IN BOOLEAN Wait)
+LONG 
+STDCALL
+KeReadStateMutant(IN PKMUTANT Mutant)
 {
-  KIRQL OldIrql;
-
-  OldIrql = KeAcquireDispatcherDatabaseLock();
-  if (Mutex->OwnerThread != KeGetCurrentThread())
-    {
-      DbgPrint("THREAD_NOT_MUTEX_OWNER: Mutex %p\n", Mutex);
-      KEBUGCHECK(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);
-    }
-
-  if (Wait == FALSE)
-    {
-      KeReleaseDispatcherDatabaseLock(OldIrql);
-    }
-  else
-    {
-      KTHREAD *Thread = KeGetCurrentThread();
-      Thread->WaitNext = Wait;
-      Thread->WaitIrql = OldIrql;
-    }
-
-  return(0);
+    /* Return the Signal State */
+    return(Mutant->Header.SignalState);
 }
 
 /*
  * @implemented
  */
-NTSTATUS STDCALL
-KeWaitForMutexObject(IN PKMUTEX Mutex,
-                    IN KWAIT_REASON WaitReason,
-                    IN KPROCESSOR_MODE WaitMode,
-                    IN BOOLEAN Alertable,
-                    IN PLARGE_INTEGER Timeout)
+LONG
+STDCALL
+KeReadStateMutex(IN PKMUTEX Mutex)
 {
-  return(KeWaitForSingleObject(Mutex,WaitReason,WaitMode,Alertable,Timeout));
+    /* Return the Signal State */
+    return(Mutex->Header.SignalState);
 }
 
-
 /*
  * @implemented
  */
-VOID STDCALL
-KeInitializeMutant(IN PKMUTANT Mutant,
-                  IN BOOLEAN InitialOwner)
+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;
+
+    /* 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;
 }
 
 /*
  * @implemented
  */
-LONG STDCALL
-KeReadStateMutant(IN PKMUTANT Mutant)
+LONG 
+STDCALL
+KeReleaseMutex(IN PKMUTEX Mutex,
+               IN BOOLEAN Wait)
 {
-  return(Mutant->Header.SignalState);
+
+    /* There's no difference at this level between the two */
+    return KeReleaseMutant(Mutex, IO_NO_INCREMENT, FALSE, Wait);
 }
 
 /*
  * @implemented
  */
-LONG STDCALL
-KeReleaseMutant(IN PKMUTANT Mutant,
-               IN KPRIORITY Increment,
-               IN BOOLEAN Abandon,
-               IN BOOLEAN Wait)
+NTSTATUS 
+STDCALL
+KeWaitForMutexObject(IN PKMUTEX Mutex,
+                     IN KWAIT_REASON WaitReason,
+                     IN KPROCESSOR_MODE WaitMode,
+                     IN BOOLEAN Alertable,
+                     IN PLARGE_INTEGER Timeout)
 {
-  KIRQL OldIrql;
-
-  OldIrql = KeAcquireDispatcherDatabaseLock();
-  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(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);
-    }
-
-  if (Wait == FALSE)
-    {
-      KeReleaseDispatcherDatabaseLock(OldIrql);
-    }
-  else
-    {
-      KTHREAD *Thread = KeGetCurrentThread();
-      Thread->WaitNext = Wait;
-      Thread->WaitIrql = OldIrql;
-    }
-
-  return(0);
+    /* This is a simple macro. Export the function here though */
+    return KeWaitForSingleObject(Mutex,
+                                 WaitReason,
+                                 WaitMode,
+                                 Alertable,
+                                 Timeout);
 }
 
 /* EOF */