Sync to trunk head (r42241)
[reactos.git] / reactos / ntoskrnl / ke / spinlock.c
index 5681947..a1ed59b 100644 (file)
-/* $Id: spinlock.c,v 1.18 2003/07/21 21:53:51 royce Exp $
- *
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            ntoskrnl/ke/spinlock.c
- * PURPOSE:         Implements spinlocks
- * PROGRAMMER:      David Welch (welch@cwcom.net)
- * UPDATE HISTORY:
- *                  3/6/98: Created
+ * PURPOSE:         Spinlock and Queued Spinlock Support
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  */
 
+/* INCLUDES ******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+#define LQ_WAIT     1
+#define LQ_OWN      2
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+VOID
+FASTCALL
+KeAcquireQueuedSpinLockAtDpcLevel(IN PKSPIN_LOCK_QUEUE LockHandle)
+{
+#ifdef CONFIG_SMP
+    PKSPIN_LOCK_QUEUE Prev;
+
+    /* Set the new lock */
+    Prev = (PKSPIN_LOCK_QUEUE)
+           InterlockedExchange((PLONG)LockHandle->Next,
+                               (LONG)LockHandle);
+    if (!Prev)
+    {
+        /* There was nothing there before. We now own it */
+         *LockHandle->Lock |= LQ_OWN;
+        return;
+    }
+
+    /* Set the wait flag */
+     *LockHandle->Lock |= LQ_WAIT;
+
+    /* Link us */
+    Prev->Next = (PKSPIN_LOCK_QUEUE)LockHandle;
+
+    /* Loop and wait */
+    while (*LockHandle->Lock & LQ_WAIT)
+        YieldProcessor();
+#endif
+}
+
+VOID
+FASTCALL
+KeReleaseQueuedSpinLockFromDpcLevel(IN PKSPIN_LOCK_QUEUE LockHandle)
+{
+#ifdef CONFIG_SMP
+    KSPIN_LOCK LockVal;
+    PKSPIN_LOCK_QUEUE Waiter;
+
+    /* Remove own and wait flags */
+    *LockHandle->Lock &= ~(LQ_OWN | LQ_WAIT);
+    LockVal = *LockHandle->Lock;
+
+    /* Check if we already own it */
+    if (LockVal == (KSPIN_LOCK)LockHandle)
+    {
+        /* Disown it */
+        LockVal = (KSPIN_LOCK)
+                  InterlockedCompareExchangePointer(LockHandle->Lock,
+                                                    NULL,
+                                                    LockHandle);
+    }
+    if (LockVal == (KSPIN_LOCK)LockHandle) return;
+
+    /* Need to wait for it */
+    Waiter = LockHandle->Next;
+    while (!Waiter)
+    {
+        YieldProcessor();
+        Waiter = LockHandle->Next;
+    }
+
+    /* It's gone */
+    *(ULONG_PTR*)&Waiter->Lock ^= (LQ_OWN | LQ_WAIT);
+    LockHandle->Next = NULL;
+#endif
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+#ifdef _X86_
 /*
- * NOTE: On a uniprocessor machine spinlocks are implemented by raising
- * the irq level
+ * @implemented
  */
+KIRQL
+NTAPI
+KeAcquireInterruptSpinLock(IN PKINTERRUPT Interrupt)
+{
+    KIRQL OldIrql;
 
-/* INCLUDES ****************************************************************/
+    /* Raise IRQL */
+    KeRaiseIrql(Interrupt->SynchronizeIrql, &OldIrql);
 
-#include <ddk/ntddk.h>
-#include <roscfg.h>
+    /* Acquire spinlock on MP */
+    KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
+    return OldIrql;
+}
 
-#include <internal/debug.h>
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KeReleaseInterruptSpinLock(IN PKINTERRUPT Interrupt,
+                           IN KIRQL OldIrql)
+{
+    /* Release lock on MP */
+    KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
 
-/* FUNCTIONS ***************************************************************/
+    /* Lower IRQL */
+    KeLowerIrql(OldIrql);
+}
 
 /*
  * @implemented
  */
-BOOLEAN STDCALL
-KeSynchronizeExecution (PKINTERRUPT            Interrupt,
-                       PKSYNCHRONIZE_ROUTINE   SynchronizeRoutine,
-                       PVOID                   SynchronizeContext)
+VOID
+NTAPI
+_KeInitializeSpinLock(IN PKSPIN_LOCK SpinLock)
+{
+    /* Clear it */
+    *SpinLock = 0;
+}
+#endif
+
 /*
- * FUNCTION: Synchronizes the execution of a given routine with the ISR
- * of a given interrupt object
- * ARGUMENTS:
- *       Interrupt = Interrupt object to synchronize with
- *       SynchronizeRoutine = Routine to call whose execution is 
- *                            synchronized with the ISR
- *       SynchronizeContext = Parameter to pass to the synchronized routine
- * RETURNS: TRUE if the operation succeeded
+ * @implemented
  */
+#undef KeAcquireSpinLockAtDpcLevel
+VOID
+NTAPI
+KeAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock)
 {
-   KIRQL oldlvl;
-   BOOLEAN ret;
-   
-   KeRaiseIrql(Interrupt->SynchLevel,&oldlvl);
-   KeAcquireSpinLockAtDpcLevel(Interrupt->IrqLock);
-   
-   ret = SynchronizeRoutine(SynchronizeContext);
-   
-   KeReleaseSpinLockFromDpcLevel(Interrupt->IrqLock);
-   KeLowerIrql(oldlvl);
-   
-   return(ret);
+    /* Do the inlined function */
+    KxAcquireSpinLock(SpinLock);
 }
 
 /*
  * @implemented
  */
-VOID STDCALL
-KeInitializeSpinLock (PKSPIN_LOCK      SpinLock)
+#undef KeReleaseSpinLockFromDpcLevel
+VOID
+NTAPI
+KeReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock)
+{
+    /* Do the lined function */
+    KxReleaseSpinLock(SpinLock);
+}
+
 /*
- * FUNCTION: Initalizes a spinlock
- * ARGUMENTS:
- *           SpinLock = Caller supplied storage for the spinlock
+ * @implemented
  */
+VOID
+FASTCALL
+KefAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock)
 {
-   *SpinLock = 0;
+    /* Do the inlined function */
+    KxAcquireSpinLock(SpinLock);
 }
 
-#undef KeAcquireSpinLockAtDpcLevel
+/*
+ * @implemented
+ */
+VOID
+FASTCALL
+KefReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock)
+{
+    /* Do the lined function */
+    KxReleaseSpinLock(SpinLock);
+}
 
 /*
  * @implemented
  */
-VOID STDCALL
-KeAcquireSpinLockAtDpcLevel (PKSPIN_LOCK       SpinLock)
-/*
- * FUNCTION: Acquires a spinlock when the caller is already running at 
- * dispatch level
- * ARGUMENTS:
- *        SpinLock = Spinlock to acquire
- */
-{
-   ULONG i;
-
-   /*
-    * FIXME: This depends on gcc assembling this test to a single load from
-    * the spinlock's value.
-    */
-   if (*SpinLock >= 2)
-     {
-       DbgPrint("Lock %x has bad value %x\n", SpinLock, *SpinLock);
-       KEBUGCHECK(0);
-     }
-   
-   while ((i = InterlockedExchange((LONG *)SpinLock, 1)) == 1)
-     {
-#ifndef MP
-       DbgPrint("Spinning on spinlock %x current value %x\n", SpinLock, i);
-       KEBUGCHECK(0);
-#else /* not MP */
-       /* Avoid reading the value again too fast */
-#endif /* MP */
-     }
+VOID
+FASTCALL
+KiAcquireSpinLock(IN PKSPIN_LOCK SpinLock)
+{
+    /* Do the inlined function */
+    KxAcquireSpinLock(SpinLock);
 }
 
-#undef KeReleaseSpinLockFromDpcLevel
+/*
+ * @implemented
+ */
+VOID
+FASTCALL
+KiReleaseSpinLock(IN PKSPIN_LOCK SpinLock)
+{
+    /* Do the lined function */
+    KxReleaseSpinLock(SpinLock);
+}
 
 /*
  * @implemented
  */
-VOID STDCALL
-KeReleaseSpinLockFromDpcLevel (PKSPIN_LOCK     SpinLock)
+BOOLEAN
+FASTCALL
+KeTryToAcquireSpinLockAtDpcLevel(IN OUT PKSPIN_LOCK SpinLock)
+{
+#ifdef CONFIG_SMP
+    /* Check if it's already acquired */
+    if (!(*SpinLock))
+    {
+        /* Try to acquire it */
+        if (InterlockedBitTestAndSet((PLONG)SpinLock, 0))
+        {
+            /* Someone else acquired it */
+            return FALSE;
+        }
+    }
+    else
+    {
+        /* It was already acquired */
+        return FALSE;
+    }
+
+#if DBG
+    /* On debug builds, we OR in the KTHREAD */
+    *SpinLock = (ULONG_PTR)KeGetCurrentThread() | 1;
+#endif
+#endif
+
+    /* All is well, return TRUE */
+    return TRUE;
+}
+
 /*
- * FUNCTION: Releases a spinlock when the caller was running at dispatch
- * level before acquiring it
- * ARGUMENTS: 
- *         SpinLock = Spinlock to release
+ * @implemented
  */
+VOID
+FASTCALL
+KeAcquireInStackQueuedSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock,
+                                         IN PKLOCK_QUEUE_HANDLE LockHandle)
 {
-   if (*SpinLock != 1)
-     {
-       DbgPrint("Releasing unacquired spinlock %x\n", SpinLock);
-       KEBUGCHECK(0);
-     }
-   (void)InterlockedExchange((LONG *)SpinLock, 0);
+#ifdef CONFIG_SMP
+    /* Set it up properly */
+    LockHandle->LockQueue.Next = NULL;
+    LockHandle->LockQueue.Lock = SpinLock;
+    KeAcquireQueuedSpinLockAtDpcLevel(LockHandle->LockQueue.Next);
+#endif
+}
+
+/*
+ * @implemented
+ */
+VOID
+FASTCALL
+KeReleaseInStackQueuedSpinLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE LockHandle)
+{
+#ifdef CONFIG_SMP
+    /* Call the internal function */
+    KeReleaseQueuedSpinLockFromDpcLevel(LockHandle->LockQueue.Next);
+#endif
+}
+
+/*
+ * @unimplemented
+ */
+KIRQL
+FASTCALL
+KeAcquireSpinLockForDpc(IN PKSPIN_LOCK SpinLock)
+{
+    UNIMPLEMENTED;
+    return 0;
+}
+
+/*
+ * @unimplemented
+ */
+VOID
+FASTCALL
+KeReleaseSpinLockForDpc(IN PKSPIN_LOCK SpinLock,
+                        IN KIRQL OldIrql)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @unimplemented
+ */
+KIRQL
+FASTCALL
+KeAcquireInStackQueuedSpinLockForDpc(IN PKSPIN_LOCK SpinLock,
+                                     IN PKLOCK_QUEUE_HANDLE LockHandle)
+{
+    UNIMPLEMENTED;
+    return 0;
+}
+
+/*
+ * @unimplemented
+ */
+VOID
+FASTCALL
+KeReleaseInStackQueuedSpinLockForDpc(IN PKLOCK_QUEUE_HANDLE LockHandle)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+FASTCALL
+KeTestSpinLock(IN PKSPIN_LOCK SpinLock)
+{
+    /* Test this spinlock */
+    if (*SpinLock)
+    {
+        /* Spinlock is busy, yield execution */
+        YieldProcessor();
+
+        /* Return busy flag */
+        return FALSE;
+    }
+
+    /* Spinlock appears to be free */
+    return TRUE;
 }
 
 /* EOF */