diff --git a/reactos/include/funcs.h b/reactos/include/funcs.h index 852e2269674..8b7cdbfdce2 100644 --- a/reactos/include/funcs.h +++ b/reactos/include/funcs.h @@ -4623,12 +4623,12 @@ InterlockedExchange( LONG Value ); -PVOID +LONG STDCALL InterlockedCompareExchange( - PVOID *Destination, - PVOID Exchange, - PVOID Comperand + PLONG Destination, + LONG Exchange, + LONG Comperand ); #endif diff --git a/reactos/include/ntdll/rtl.h b/reactos/include/ntdll/rtl.h index be680f2d20f..c400129f009 100644 --- a/reactos/include/ntdll/rtl.h +++ b/reactos/include/ntdll/rtl.h @@ -1,4 +1,4 @@ -/* $Id: rtl.h,v 1.53 2004/11/29 00:05:31 gdalsnes Exp $ +/* $Id$ * */ @@ -100,7 +100,7 @@ typedef struct _CRITICAL_SECTION_DEBUG ULONG EntryCount; ULONG ContentionCount; ULONG Depth; - PVOID OwnerBackTrace[ 5 ]; + PVOID Spare[ 2 ]; } CRITICAL_SECTION_DEBUG, *PCRITICAL_SECTION_DEBUG; @@ -190,13 +190,13 @@ RtlAddAuditAccessAceEx(IN OUT PACL Acl, IN BOOLEAN Success, IN BOOLEAN Failure); -VOID STDCALL +NTSTATUS STDCALL RtlDeleteCriticalSection (PCRITICAL_SECTION CriticalSection); WCHAR STDCALL RtlDowncaseUnicodeChar(IN WCHAR Source); -VOID STDCALL +NTSTATUS STDCALL RtlEnterCriticalSection (PCRITICAL_SECTION CriticalSection); NTSTATUS STDCALL @@ -211,7 +211,7 @@ RtlInt64ToUnicodeString (IN ULONGLONG Value, IN ULONG Base, PUNICODE_STRING String); -VOID STDCALL +NTSTATUS STDCALL RtlLeaveCriticalSection (PCRITICAL_SECTION CriticalSection); BOOLEAN STDCALL @@ -744,12 +744,12 @@ InterlockedExchange ( LONG Value ); -PVOID +LONG STDCALL InterlockedCompareExchange ( - PVOID *Destination, - PVOID Exchange, - PVOID Comperand + PLONG Destination, + LONG Exchange, + LONG Comperand ); LONG @@ -759,6 +759,26 @@ InterlockedExchangeAdd ( LONG Increment ); +#ifndef InterlockedExchangePointer + #ifdef _WIN64 + #define InterlockedExchangePointer(Target, Value) \ + (PVOID)InterlockedExchange64((PLONGLONG)(Target), (LONGLONG)(Value)) + #else + #define InterlockedExchangePointer(Target, Value) \ + (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value)) + #endif +#endif + +#ifndef InterlockedCompareExchangePointer + #ifdef _WIN64 + #define InterlockedCompareExchangePointer(Target, Exchange, Comperand) \ + (PVOID)InterlockedCompareExchange64((PLONGLONG)(Target), (LONGLONG)(Exchange), (LONGLONG)(Comperand)) + #else + #define InterlockedCompareExchangePointer(Target, Exchange, Comperand) \ + (PVOID)InterlockedCompareExchange((PLONG)Target, (LONG)Exchange, (LONG)Comperand) + #endif +#endif + #endif /* __INTERLOCKED_DECLARED */ #endif /* __NTDRIVER__ */ diff --git a/reactos/lib/ntdll/rtl/critical.c b/reactos/lib/ntdll/rtl/critical.c index b856f548312..94a533179b9 100644 --- a/reactos/lib/ntdll/rtl/critical.c +++ b/reactos/lib/ntdll/rtl/critical.c @@ -1,4 +1,4 @@ -/* $Id: critical.c,v 1.20 2004/03/24 23:43:52 gdalsnes Exp $ +/* $Id$ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries @@ -19,160 +19,391 @@ /* FUNCTIONS *****************************************************************/ -/* - * @implemented - */ -VOID STDCALL -RtlDeleteCriticalSection(PCRITICAL_SECTION CriticalSection) +inline static HANDLE RtlGetCurrentThreadId() { - NtClose(CriticalSection->LockSemaphore); - CriticalSection->LockCount = -1; + return (HANDLE)NtCurrentTeb()->Cid.UniqueThread; } -/* - * @implemented - */ -DWORD STDCALL -RtlSetCriticalSectionSpinCount( - LPCRITICAL_SECTION CriticalSection, - DWORD SpinCount - ) +inline static void small_pause(void) { - -#ifdef MP - DWORD PrevSpinCount = CriticalSection->SpinCount; - CriticalSection->SpinCount = SpinCount; - return PrevSpinCount; +#ifdef __i386__ + __asm__ __volatile__( "rep;nop" : : : "memory" ); #else - return 0; + __asm__ __volatile__( "" : : : "memory" ); #endif - } - -/* - * @implemented +/*********************************************************************** + * get_semaphore */ -VOID STDCALL -RtlEnterCriticalSection(PCRITICAL_SECTION CriticalSection) +static inline HANDLE get_semaphore( PCRITICAL_SECTION crit ) { - HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread; - - if (InterlockedIncrement(&CriticalSection->LockCount)) - { - NTSTATUS Status; - - if (CriticalSection->OwningThread == Thread) - { - CriticalSection->RecursionCount++; - return; - } - - DPRINT("Entering wait for critical section\n"); - Status = NtWaitForSingleObject(CriticalSection->LockSemaphore, - 0, FALSE); - if (!NT_SUCCESS(Status)) - { - DPRINT1("RtlEnterCriticalSection: Failed to wait (Status %x)\n", - Status); - } - DPRINT("Left wait for critical section\n"); - } - CriticalSection->OwningThread = Thread; - CriticalSection->RecursionCount = 1; + HANDLE ret = crit->LockSemaphore; + if (!ret) + { + HANDLE sem; + if (!NT_SUCCESS(NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 ))) return 0; + if (!(ret = (HANDLE)InterlockedCompareExchangePointer( (PVOID *)&crit->LockSemaphore, + (PVOID)sem, 0 ))) + ret = sem; + else + NtClose(sem); /* somebody beat us to it */ + } + return ret; } - -/* - * @implemented +/*********************************************************************** + * RtlInitializeCriticalSection (NTDLL.@) + * + * Initialises a new critical section. + * + * PARAMS + * crit [O] Critical section to initialise + * + * RETURNS + * STATUS_SUCCESS. + * + * SEE + * RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(), + * RtlEnterCriticalSection(), RtlLeaveCriticalSection(), + * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount() */ -NTSTATUS STDCALL -RtlInitializeCriticalSection(PCRITICAL_SECTION CriticalSection) +NTSTATUS STDCALL RtlInitializeCriticalSection( PCRITICAL_SECTION crit ) { - return RtlInitializeCriticalSectionAndSpinCount (CriticalSection, - 0); + return RtlInitializeCriticalSectionAndSpinCount( crit, 0 ); } - -/* - * @implemented +/*********************************************************************** + * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@) + * + * Initialises a new critical section with a given spin count. + * + * PARAMS + * crit [O] Critical section to initialise + * spincount [I] Spin count for crit + * + * RETURNS + * STATUS_SUCCESS. + * + * NOTES + * Available on NT4 SP3 or later. + * + * SEE + * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(), + * RtlEnterCriticalSection(), RtlLeaveCriticalSection(), + * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount() */ -VOID STDCALL -RtlLeaveCriticalSection(PCRITICAL_SECTION CriticalSection) +NTSTATUS STDCALL RtlInitializeCriticalSectionAndSpinCount( PCRITICAL_SECTION crit, ULONG spincount ) { - HANDLE Thread = (HANDLE)NtCurrentTeb()->Cid.UniqueThread; - - if (CriticalSection->OwningThread != Thread) - { - DPRINT1("Freeing critical section not owned\n"); - } - - CriticalSection->RecursionCount--; - if (CriticalSection->RecursionCount > 0) - { - InterlockedDecrement(&CriticalSection->LockCount); - return; - } - CriticalSection->OwningThread = 0; - if (InterlockedDecrement(&CriticalSection->LockCount) >= 0) - { - NTSTATUS Status; - - Status = NtReleaseSemaphore(CriticalSection->LockSemaphore, 1, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to release semaphore (Status %x)\n", - Status); - } - } + /* Does ROS need this, or is this special to Wine? And if ros need it, should + it be enabled in the release build? -Gunnar */ + if (RtlGetProcessHeap()) crit->DebugInfo = NULL; + else + { + crit->DebugInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(CRITICAL_SECTION_DEBUG)); + if (crit->DebugInfo) + { + crit->DebugInfo->Type = 0; + crit->DebugInfo->CreatorBackTraceIndex = 0; + crit->DebugInfo->CriticalSection = crit; + crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList); + crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList); + crit->DebugInfo->EntryCount = 0; + crit->DebugInfo->ContentionCount = 0; + crit->DebugInfo->Spare[0] = 0; + crit->DebugInfo->Spare[1] = 0; + } + } + crit->LockCount = -1; + crit->RecursionCount = 0; + crit->OwningThread = 0; + crit->LockSemaphore = 0; + crit->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? spincount : 0; + return STATUS_SUCCESS; } -/* - * @implemented +/*********************************************************************** + * RtlSetCriticalSectionSpinCount (NTDLL.@) + * + * Sets the spin count of a critical section. + * + * PARAMS + * crit [I/O] Critical section + * spincount [I] Spin count for crit + * + * RETURNS + * The previous spin count. + * + * NOTES + * If the system is not SMP, spincount is ignored and set to 0. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() */ -BOOLEAN STDCALL -RtlTryEnterCriticalSection(PCRITICAL_SECTION CriticalSection) +ULONG STDCALL RtlSetCriticalSectionSpinCount( PCRITICAL_SECTION crit, ULONG spincount ) { - if (InterlockedCompareExchange((PVOID*)&CriticalSection->LockCount, - (PVOID)0, (PVOID)-1 ) == (PVOID)-1) - { - CriticalSection->OwningThread = - (HANDLE) NtCurrentTeb()->Cid.UniqueThread; - CriticalSection->RecursionCount = 1; - return TRUE; - } - if (CriticalSection->OwningThread == - (HANDLE)NtCurrentTeb()->Cid.UniqueThread) - { - InterlockedIncrement(&CriticalSection->LockCount); - CriticalSection->RecursionCount++; - return TRUE; - } - return FALSE; + ULONG oldspincount = crit->SpinCount; + crit->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? spincount : 0; + return oldspincount; } - -/* - * @implemented +/*********************************************************************** + * RtlDeleteCriticalSection (NTDLL.@) + * + * Frees the resources used by a critical section. + * + * PARAMS + * crit [I/O] Critical section to free + * + * RETURNS + * STATUS_SUCCESS. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() */ -NTSTATUS STDCALL -RtlInitializeCriticalSectionAndSpinCount (PCRITICAL_SECTION CriticalSection, - ULONG SpinCount) +NTSTATUS STDCALL RtlDeleteCriticalSection( PCRITICAL_SECTION crit ) { - CriticalSection->LockCount = -1; - CriticalSection->RecursionCount = 0; - CriticalSection->OwningThread = (HANDLE)0; -#ifdef MP - CriticalSection->SpinCount = SpinCount; -#else - CriticalSection->SpinCount = 0; -#endif - - return NtCreateSemaphore (&CriticalSection->LockSemaphore, - SEMAPHORE_ALL_ACCESS, - NULL, - 0, - 1); + crit->LockCount = -1; + crit->RecursionCount = 0; + crit->OwningThread = (HANDLE)0; + if (crit->LockSemaphore) + NtClose( crit->LockSemaphore ); + crit->LockSemaphore = 0; + if (crit->DebugInfo) + { + /* only free the ones we made in here */ + if (!crit->DebugInfo->Spare[1]) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, crit->DebugInfo ); + crit->DebugInfo = NULL; + } + } + return STATUS_SUCCESS; } + +/*********************************************************************** + * RtlpWaitForCriticalSection (NTDLL.@) + * + * Waits for a busy critical section to become free. + * + * PARAMS + * crit [I/O] Critical section to wait for + * + * RETURNS + * STATUS_SUCCESS. + * + * NOTES + * Use RtlEnterCriticalSection() instead of this function as it is often much + * faster. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() + */ +NTSTATUS STDCALL RtlpWaitForCriticalSection( PCRITICAL_SECTION crit ) +{ + for (;;) + { + EXCEPTION_RECORD rec; + HANDLE sem = get_semaphore( crit ); + LARGE_INTEGER time; + NTSTATUS status; + + time.QuadPart = -5000 * 10000; /* 5 seconds */ + status = NtWaitForSingleObject( sem, FALSE, &time ); + if ( status == STATUS_TIMEOUT ) + { + const char *name = NULL; + if (crit->DebugInfo) name = (char *)crit->DebugInfo->Spare[1]; + if (!name) name = "?"; + DPRINT1( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (60 sec)\n", + crit, name, RtlGetCurrentThreadId(), (DWORD)crit->OwningThread ); + time.QuadPart = -60000 * 10000; + status = NtWaitForSingleObject( sem, FALSE, &time ); + if ( status == STATUS_TIMEOUT /*&& TRACE_ON(relay)*/ ) + { + DPRINT1( "section %p %s wait timed out in thread %04lx, blocked by %04lx, retrying (5 min)\n", + crit, name, RtlGetCurrentThreadId(), (DWORD) crit->OwningThread ); + time.QuadPart = -300000 * (ULONGLONG)10000; + status = NtWaitForSingleObject( sem, FALSE, &time ); + } + } + if (status == STATUS_WAIT_0) return STATUS_SUCCESS; + + /* Throw exception only for Wine internal locks */ + if ((!crit->DebugInfo) || (!crit->DebugInfo->Spare[1])) continue; + + rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; + rec.ExceptionFlags = 0; + rec.ExceptionRecord = NULL; + rec.ExceptionAddress = RtlRaiseException; /* sic */ + rec.NumberParameters = 1; + rec.ExceptionInformation[0] = (DWORD)crit; + RtlRaiseException( &rec ); + } +} + + +/*********************************************************************** + * RtlpUnWaitCriticalSection (NTDLL.@) + * + * Notifies other threads waiting on the busy critical section that it has + * become free. + * + * PARAMS + * crit [I/O] Critical section + * + * RETURNS + * Success: STATUS_SUCCESS. + * Failure: Any error returned by NtReleaseSemaphore() + * + * NOTES + * Use RtlLeaveCriticalSection() instead of this function as it is often much + * faster. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() + */ +NTSTATUS STDCALL RtlpUnWaitCriticalSection( PCRITICAL_SECTION crit ) +{ + HANDLE sem = get_semaphore( crit ); + NTSTATUS res = NtReleaseSemaphore( sem, 1, NULL ); + if (!NT_SUCCESS(res)) RtlRaiseStatus( res ); + return res; +} + + +/*********************************************************************** + * RtlEnterCriticalSection (NTDLL.@) + * + * Enters a critical section, waiting for it to become available if necessary. + * + * PARAMS + * crit [I/O] Critical section to enter + * + * RETURNS + * STATUS_SUCCESS. The critical section is held by the caller. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(), + * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() + */ +NTSTATUS STDCALL RtlEnterCriticalSection( PCRITICAL_SECTION crit ) +{ + if (crit->SpinCount) + { + ULONG count; + + if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS; + for (count = crit->SpinCount; count > 0; count--) + { + if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */ + if (crit->LockCount == -1) /* try again */ + { + if (InterlockedCompareExchange(&crit->LockCount, 0,-1 ) == -1) goto done; + } + small_pause(); + } + } + + if (InterlockedIncrement( &crit->LockCount )) + { + if (crit->OwningThread == (HANDLE)RtlGetCurrentThreadId()) + { + crit->RecursionCount++; + return STATUS_SUCCESS; + } + + /* Now wait for it */ + RtlpWaitForCriticalSection( crit ); + } +done: + crit->OwningThread = (HANDLE)RtlGetCurrentThreadId(); + crit->RecursionCount = 1; + return STATUS_SUCCESS; +} + + +/*********************************************************************** + * RtlTryEnterCriticalSection (NTDLL.@) + * + * Tries to enter a critical section without waiting. + * + * PARAMS + * crit [I/O] Critical section to enter + * + * RETURNS + * Success: TRUE. The critical section is held by the caller. + * Failure: FALSE. The critical section is currently held by another thread. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount() + */ +BOOLEAN STDCALL RtlTryEnterCriticalSection( PCRITICAL_SECTION crit ) +{ + BOOL ret = FALSE; + if (InterlockedCompareExchange(&crit->LockCount, 0L, -1 ) == -1) + { + crit->OwningThread = (HANDLE)RtlGetCurrentThreadId(); + crit->RecursionCount = 1; + ret = TRUE; + } + else if (crit->OwningThread == (HANDLE)RtlGetCurrentThreadId()) + { + InterlockedIncrement( &crit->LockCount ); + crit->RecursionCount++; + ret = TRUE; + } + return ret; +} + + +/*********************************************************************** + * RtlLeaveCriticalSection (NTDLL.@) + * + * Leaves a critical section. + * + * PARAMS + * crit [I/O] Critical section to leave. + * + * RETURNS + * STATUS_SUCCESS. + * + * SEE + * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), + * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), + * RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection() + */ +NTSTATUS STDCALL RtlLeaveCriticalSection( PCRITICAL_SECTION crit ) +{ + if (crit->OwningThread != RtlGetCurrentThreadId()) + { + DPRINT1("Freeing critical section not owned\n"); + } + + if (--crit->RecursionCount) InterlockedDecrement( &crit->LockCount ); + else + { + crit->OwningThread = 0; + if (InterlockedDecrement( &crit->LockCount ) >= 0) + { + /* someone is waiting */ + RtlpUnWaitCriticalSection( crit ); + } + } + return STATUS_SUCCESS; +} + + /* EOF */