fixed some bugs in ExWaitForRundownProtectionRelease() and ExReleaseRundownProtectionEx()
Modified: trunk/reactos/ntoskrnl/ex/rundown.c

Modified: trunk/reactos/ntoskrnl/ex/rundown.c
--- trunk/reactos/ntoskrnl/ex/rundown.c	2005-02-20 22:40:30 UTC (rev 13698)
+++ trunk/reactos/ntoskrnl/ex/rundown.c	2005-02-20 23:56:47 UTC (rev 13699)
@@ -1,4 +1,4 @@
-/* $Id:$
+/* $Id$
  * 
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
@@ -117,24 +117,28 @@
         {
             /* Get Pointer */
             PRUNDOWN_DESCRIPTOR RundownDescriptor = (PRUNDOWN_DESCRIPTOR)((ULONG_PTR)RunRef->Ptr & ~EX_RUNDOWN_ACTIVE);
+            
+            ASSERT(RundownDescriptor != NULL);
+            
+            Current = RundownDescriptor->References;
 
-            /* Decrease Reference Count by RundownDescriptor->References */
+            /* Decrease RundownDescriptor->References by Count references */
             for (;;)
             {
                 ULONG_PTR PrevCount, NewCount;
 
-                if ((Current >> EX_RUNDOWN_COUNT_SHIFT) == RundownDescriptor->References)
+                if ((Count >> EX_RUNDOWN_COUNT_SHIFT) == Current)
                 {
                     NewCount = 0;
                 }
                 else
                 {
-                    NewCount = (((Current >> EX_RUNDOWN_COUNT_SHIFT) - RundownDescriptor->References) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE;
+                    NewCount = ((RundownDescriptor->References - (Count >> EX_RUNDOWN_COUNT_SHIFT)) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE;
                 }
 #ifdef _WIN64
-                PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current);
+                PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RundownDescriptor->References, (LONGLONG)NewCount, (LONGLONG)Current);
 #else
-                PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current);
+                PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RundownDescriptor->References, (LONG)NewCount, (LONG)Current);
 #endif
                 if (PrevCount == Current)
                 {
@@ -212,40 +216,45 @@
     ULONG_PTR PrevCount, NewPtr, PrevPtr;
     RUNDOWN_DESCRIPTOR RundownDescriptor;
     
-#ifdef _WIN64
-    PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)EX_RUNDOWN_ACTIVE, 0LL);
-#else
-    PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, EX_RUNDOWN_ACTIVE, 0);
-#endif
-
-    if (PrevCount == 0 ||
-        PrevCount & EX_RUNDOWN_ACTIVE)
-    {
-        return;
-    }
+    PrevCount = RunRef->Count;
     
-    /* save the reference counter */
-    RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
-    
-    /* Pending references... wait on them to be closed with an event */
-    KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
-    
-    NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
-    PrevCount = EX_RUNDOWN_ACTIVE;
-    
-    do
+    if (PrevCount != 0 && !(PrevCount & EX_RUNDOWN_ACTIVE))
     {
+        /* save the reference counter */
+        RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
+
+        /* Pending references... wait on them to be closed with an event */
+        KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE);
+        
+        ASSERT(!((ULONG_PTR)&RundownDescriptor & EX_RUNDOWN_ACTIVE));
+        
+        NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE;
+        
+        for (;;)
+        {
 #ifdef _WIN64
-        PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount);
+            PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount);
 #else
-        PrevPtr = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, (LONG)NewPtr, (LONG)PrevCount);
+            PrevPtr = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Ptr, (LONG)NewPtr, (LONG)PrevCount);
 #endif
-
-        PrevCount = PrevPtr;
-    } while (PrevPtr != PrevCount);
-    
-    /* Wait for whoever needs to release to notify us */
-    KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
+            if (PrevPtr == PrevCount)
+            {
+                /* Wait for whoever needs to release to notify us */
+                KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL);
+                break;
+            }
+            else if (PrevPtr == 0 || (PrevPtr & EX_RUNDOWN_ACTIVE))
+            {
+                /* some one else was faster, let's just bail */
+                break;
+            }
+            
+            PrevCount = PrevPtr;
+            
+            /* save the changed reference counter and try again */
+            RundownDescriptor.References = PrevCount >> EX_RUNDOWN_COUNT_SHIFT;
+        }
+    }
 }
 
 /* EOF */