KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / mutex.c
index b2c24cc..2ff0507 100644 (file)
 /*
- *  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.9 2001/11/04 00:17:24 ekohl 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 <ddk/ntddk.h>
-#include <internal/ke.h>
-#include <internal/id.h>
-
+#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->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);
-   Mutex->Header.SignalState++;
-   assert(Mutex->Header.SignalState <= 1);
-   if (Mutex->Header.SignalState == 1)
-     {
-       KeDispatcherObjectWake(&Mutex->Header);
-     }
-   KeReleaseDispatcherDatabaseLock(Wait);
-   return(0);
+    /* Return the Signal State */
+    return(Mutant->Header.SignalState);
 }
 
-NTSTATUS STDCALL
-KeWaitForMutexObject(PKMUTEX           Mutex,
-                    KWAIT_REASON       WaitReason,
-                    KPROCESSOR_MODE    WaitMode,
-                    BOOLEAN            Alertable,
-                    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)
 {
-  KeInitializeDispatcherHeader(&Mutant->Header,
-                              InternalMutexType,
-                              sizeof(KMUTANT) / sizeof(ULONG),
-                              1);
-  if (InitialOwner == TRUE)
-    {
-      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
-    {
-      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;
 }
 
-LONG STDCALL
-KeReadStateMutant(IN PKMUTANT Mutant)
+/*
+ * @implemented
+ */
+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);
 }
 
-LONG STDCALL
-KeReleaseMutant(IN PKMUTANT Mutant,
-               ULONG Param2,
-               ULONG Param3,
-               IN BOOLEAN Wait)
+/*
+ * @implemented
+ */
+NTSTATUS 
+STDCALL
+KeWaitForMutexObject(IN PKMUTEX Mutex,
+                     IN KWAIT_REASON WaitReason,
+                     IN KPROCESSOR_MODE WaitMode,
+                     IN BOOLEAN Alertable,
+                     IN PLARGE_INTEGER Timeout)
 {
-  KeAcquireDispatcherDatabaseLock(Wait);
-  Mutant->Header.SignalState++;
-  assert(Mutant->Header.SignalState <= 1);
-  if (Mutant->Header.SignalState == 1)
-    {
-      KeDispatcherObjectWake(&Mutant->Header);
-    }
-  KeReleaseDispatcherDatabaseLock(Wait);
-  return(0);
+    /* This is a simple macro. Export the function here though */
+    return KeWaitForSingleObject(Mutex,
+                                 WaitReason,
+                                 WaitMode,
+                                 Alertable,
+                                 Timeout);
 }
 
 /* EOF */