diff --git a/reactos/include/ddk/extypes.h b/reactos/include/ddk/extypes.h index c801c111af8..0caef71a4b1 100644 --- a/reactos/include/ddk/extypes.h +++ b/reactos/include/ddk/extypes.h @@ -1,4 +1,4 @@ -/* $Id: extypes.h,v 1.24 2004/10/03 03:03:53 ion Exp $ */ +/* $Id: extypes.h,v 1.25 2004/10/22 22:49:00 weiden Exp $ */ #ifndef __INCLUDE_DDK_EXTYPES_H #define __INCLUDE_DDK_EXTYPES_H @@ -39,16 +39,18 @@ typedef struct _ERESOURCE } ERESOURCE, *PERESOURCE; #define EX_RUNDOWN_ACTIVE 0x1 +#define EX_RUNDOWN_COUNT_SHIFT 0x1 +#define EX_RUNDOWN_COUNT_INC (0x1 << EX_RUNDOWN_COUNT_SHIFT) typedef struct _RUNDOWN_DESCRIPTOR { - ULONG References; - PKEVENT RundownEvent; + ULONG_PTR References; + KEVENT RundownEvent; } RUNDOWN_DESCRIPTOR, *PRUNDOWN_DESCRIPTOR; typedef struct _EX_RUNDOWN_REF { union { ULONG_PTR Count; - PRUNDOWN_DESCRIPTOR Pointer; + PRUNDOWN_DESCRIPTOR Ptr; }; } EX_RUNDOWN_REF, *PEX_RUNDOWN_REF; diff --git a/reactos/ntoskrnl/ex/rundown.c b/reactos/ntoskrnl/ex/rundown.c index a673ea95249..39dfa89770e 100644 --- a/reactos/ntoskrnl/ex/rundown.c +++ b/reactos/ntoskrnl/ex/rundown.c @@ -1,6 +1,6 @@ /* * ReactOS kernel - * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team + * Copyright (C) 1998 - 2004 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 @@ -54,11 +54,27 @@ ExAcquireRundownProtectionEx ( IN ULONG Count ) { - /* Make sure a Rundown is not in progress */ - if (RunRef->Count & EX_RUNDOWN_ACTIVE) return FALSE; + ULONG_PTR PrevCount, Current; + + Count <<= EX_RUNDOWN_COUNT_SHIFT; + + /* Loop until successfully incremented the counter */ + do + { + Current = RunRef->Count; + + /* Make sure a rundown is not active */ + if (Current & EX_RUNDOWN_ACTIVE) + { + return FALSE; + } - /* Increment Reference Count */ - RunRef->Count += Count * 2; +#ifdef _WIN64 + PrevCount = (ULONG_PTR)InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, (LONGLONG)Count); +#else + PrevCount = (ULONG_PTR)InterlockedExchangeAdd((LONG*)&RunRef->Count, (LONG)Count); +#endif + } while (PrevCount != Current); /* Return Success */ return TRUE; @@ -87,7 +103,11 @@ ExReInitializeRundownProtection ( ) { /* Reset the count */ - RunRef->Count = 0; +#ifdef _WIN64 + InterlockedExchangeAdd64((LONGLONG*)&RunRef->Count, 0LL); +#else + InterlockedExchangeAdd((LONG*)&RunRef->Count, 0); +#endif } @@ -101,26 +121,67 @@ ExReleaseRundownProtectionEx ( IN ULONG Count ) { - PRUNDOWN_DESCRIPTOR RundownDescriptor; + Count <<= EX_RUNDOWN_COUNT_SHIFT; + + for (;;) + { + ULONG_PTR Current = RunRef->Count; + + /* Check if Rundown is active */ + if (Current & EX_RUNDOWN_ACTIVE) + { + /* Get Pointer */ + PRUNDOWN_DESCRIPTOR RundownDescriptor = (PRUNDOWN_DESCRIPTOR)((ULONG_PTR)RunRef->Ptr & ~EX_RUNDOWN_ACTIVE); - /* Check if Rundown is in progress */ - if (RunRef->Count & EX_RUNDOWN_ACTIVE) { + /* Decrease Reference Count by RundownDescriptor->References */ + for (;;) + { + ULONG_PTR PrevCount, NewCount; - /* Get Pointer */ - RundownDescriptor = RunRef->Pointer; + if ((Current >> EX_RUNDOWN_COUNT_SHIFT) == RundownDescriptor->References) + { + NewCount = 0; + } + else + { + NewCount = (((Current >> EX_RUNDOWN_COUNT_SHIFT) - RundownDescriptor->References) << EX_RUNDOWN_COUNT_SHIFT) | EX_RUNDOWN_ACTIVE; + } +#ifdef _WIN64 + PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current); +#else + PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current); +#endif + if (PrevCount == Current) + { + if (NewCount == 0) + { + /* Signal the event so anyone waiting on it can now kill it */ + KeSetEvent(&RundownDescriptor->RundownEvent, 0, FALSE); + } - /* Decrease Reference Count */ - RundownDescriptor->References -= Count; - - /* If anyone else is still referencing, don't signal the event */ - if (RundownDescriptor->References) return; - - /* Signal the event so anyone waiting on it can now kill it */ - KeSetEvent(RundownDescriptor->RundownEvent, 0, FALSE); - - } else { - /* Decrease Count */ - RunRef->Count -= Count * 2; + /* Successfully decremented the counter, so bail! */ + break; + } + + Current = PrevCount; + } + + break; + } + else + { + ULONG_PTR PrevCount, NewCount = Current - (ULONG_PTR)Count; +#ifdef _WIN64 + PrevCount = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)NewCount, (LONGLONG)Current); +#else + PrevCount = (ULONG_PTR)InterlockedCompareExchange((LONG*)&RunRef->Count, (LONG)NewCount, (LONG)Current); +#endif + if (PrevCount == Current) + { + /* Successfully decremented the counter, so bail! */ + break; + } + } } } @@ -134,7 +195,7 @@ ExReleaseRundownProtection ( ) { /* Call the general function with only 1 reference removal */ - ExReleaseRundownProtectionEx(RunRef, 1); + ExReleaseRundownProtectionEx(RunRef, 1); } /* @@ -146,8 +207,12 @@ ExRundownCompleted ( IN PEX_RUNDOWN_REF RunRef ) { - /* Remove pending rundown */ - RunRef->Count++; + /* mark the */ +#ifdef _WIN64 + InterlockedExchange64((LONGLONG*)&RunRef->Count, (LONGLONG)EX_RUNDOWN_ACTIVE); +#else + InterlockedExchange((LONG*)&RunRef->Count, EX_RUNDOWN_ACTIVE); +#endif } /* @@ -159,30 +224,43 @@ ExWaitForRundownProtectionRelease ( IN PEX_RUNDOWN_REF RunRef ) { + 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 - /* Check if anyone is referencing the structure */ - if (!RunRef->Count) { - /* It's free, set it to Rundown Mode */ - RunRef->Count = EX_RUNDOWN_ACTIVE; - } else { - /* Check if it's already in rundown */ - if (RunRef->Count == EX_RUNDOWN_ACTIVE) return; + if (PrevCount == 0 || + PrevCount & EX_RUNDOWN_ACTIVE) + { + return; } - - /* Save number of references */ - RundownDescriptor.References = RunRef->Count / 2; - + + /* 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); - - /* Save Rundown Descriptor. This is safe because this stack won't be modified */ - RunRef->Pointer = &RundownDescriptor; - - /* Set the Count to 1 so nobody else acquires and so release notifies us */ - RunRef->Count++; + KeInitializeEvent(&RundownDescriptor.RundownEvent, NotificationEvent, FALSE); + + NewPtr = (ULONG_PTR)&RundownDescriptor | EX_RUNDOWN_ACTIVE; + PrevCount = EX_RUNDOWN_ACTIVE; + + do + { +#ifdef _WIN64 + PrevPtr = (ULONG_PTR)InterlockedCompareExchange64((LONGLONG*)&RunRef->Ptr, (LONGLONG)NewPtr, (LONGLONG)PrevCount); +#else + 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); + KeWaitForSingleObject(&RundownDescriptor.RundownEvent, Executive, KernelMode, FALSE, NULL); } + /* EOF */