From 1aa2d22f86f01cf683cd65b6fbf7cd7c42b03ddb Mon Sep 17 00:00:00 2001 From: wj32 Date: Thu, 25 Mar 2010 06:37:04 +0000 Subject: [PATCH] * fixed handle table bugs * added PhQueryInformationHandleTable/PhSetInformationHandleTable * fixed PhfPulse(All)Condition * added Wake Event git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2979 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- 2.x/trunk/phlib/handle.c | 139 +++++++++++++++++++++++++-- 2.x/trunk/phlib/include/handlep.h | 7 +- 2.x/trunk/phlib/include/phbase.h | 35 +++++++ 2.x/trunk/phlib/include/queuedlock.h | 28 +++++- 2.x/trunk/phlib/queuedlock.c | 125 ++++++++++++++++++++++-- 5 files changed, 314 insertions(+), 20 deletions(-) diff --git a/2.x/trunk/phlib/handle.c b/2.x/trunk/phlib/handle.c index 8158476a9..c756e8101 100644 --- a/2.x/trunk/phlib/handle.c +++ b/2.x/trunk/phlib/handle.c @@ -46,7 +46,7 @@ PPH_HANDLE_TABLE PhCreateHandleTable() handleTable = PhAllocate(sizeof(PH_HANDLE_TABLE)); PhInitializeQueuedLock(&handleTable->Lock); - PhInitializeQueuedLock(&handleTable->LockedCondition); + PhInitializeQueuedLock(&handleTable->HandleWakeEvent); handleTable->NextValue = 0; @@ -148,6 +148,29 @@ VOID PhDestroyHandleTable( PhFree(HandleTable); } +VOID PhpBlockOnLockedHandleTableEntry( + __inout PPH_HANDLE_TABLE HandleTable, + __in PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ) +{ + PH_QUEUED_WAIT_BLOCK waitBlock; + ULONG_PTR value; + + PhQueueWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock); + + value = HandleTableEntry->Value; + + if (value & PH_HANDLE_TABLE_ENTRY_LOCKED) + { + // Entry has been unlocked; cancel the wait. + PhSetWakeEvent(&HandleTable->HandleWakeEvent); + } + else + { + PhWaitForWakeEvent(&HandleTable->HandleWakeEvent, &waitBlock, NULL); + } +} + VOID PhLockHandleTableEntry( __inout PPH_HANDLE_TABLE HandleTable, __inout PPH_HANDLE_TABLE_ENTRY HandleTableEntry @@ -158,7 +181,7 @@ VOID PhLockHandleTableEntry( PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT )) { - PhWaitForCondition(&HandleTable->LockedCondition, NULL, NULL); + PhpBlockOnLockedHandleTableEntry(HandleTable, HandleTableEntry); } } @@ -188,7 +211,7 @@ BOOLEAN PhLockInUseHandleTableEntry( } } - PhWaitForCondition(&HandleTable->LockedCondition, NULL, NULL); + PhpBlockOnLockedHandleTableEntry(HandleTable, HandleTableEntry); } } @@ -201,7 +224,7 @@ VOID PhUnlockHandleTableEntry( (PLONG)&HandleTableEntry->Value, PH_HANDLE_TABLE_ENTRY_LOCKED_SHIFT ); - PhPulseAllCondition(&HandleTable->LockedCondition); + PhSetWakeEvent(&HandleTable->HandleWakeEvent); } HANDLE PhCreateHandle( @@ -218,7 +241,8 @@ HANDLE PhCreateHandle( return NULL; // Copy the given handle table entry to the allocated entry. - entry->TypeAndValue.Type = PH_HANDLE_TABLE_ENTRY_IN_USE | PH_HANDLE_TABLE_ENTRY_LOCKED; + entry->TypeAndValue.Type = PH_HANDLE_TABLE_ENTRY_IN_USE; + entry->TypeAndValue.Locked = TRUE; entry->TypeAndValue.Value = HandleTableEntry->TypeAndValue.Value; entry->Value2 = HandleTableEntry->Value2; @@ -251,9 +275,9 @@ BOOLEAN PhDestroyHandle( (PVOID)(PH_HANDLE_TABLE_ENTRY_FREE | PH_HANDLE_TABLE_ENTRY_LOCKED) ); - // The handle table entry now has the lock bit cleared, so we + // The handle table entry now has the (not) locked bit set, so we // should wake waiters. - PhPulseAllCondition(&HandleTable->LockedCondition); + PhSetWakeEvent(&HandleTable->HandleWakeEvent); PhpFreeHandleTableEntry(HandleTable, handleValue, HandleTableEntry); @@ -341,6 +365,101 @@ VOID PhSweepHandleTable( } } +NTSTATUS PhQueryInformationHandleTable( + __in PPH_HANDLE_TABLE HandleTable, + __in PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + __out_bcount_opt(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG returnLength; + + switch (InformationClass) + { + case HandleTableBasicInformation: + { + PPH_HANDLE_TABLE_BASIC_INFORMATION basicInfo = Buffer; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION)) + { + basicInfo->Count = HandleTable->Count; + basicInfo->Flags = HandleTable->Flags; + basicInfo->TableLevel = HandleTable->TableValue & PH_HANDLE_TABLE_LEVEL_MASK; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + returnLength = sizeof(PH_HANDLE_TABLE_BASIC_INFORMATION); + } + break; + case HandleTableFlagsInformation: + { + PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) + { + flagsInfo->Flags = HandleTable->Flags; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + + returnLength = sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION); + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + } + + if (ReturnLength) + *ReturnLength = returnLength; + + return status; +} + +NTSTATUS PhSetInformationHandleTable( + __inout PPH_HANDLE_TABLE HandleTable, + __in PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + switch (InformationClass) + { + case HandleTableFlagsInformation: + { + PPH_HANDLE_TABLE_FLAGS_INFORMATION flagsInfo = Buffer; + ULONG flags; + + if (BufferLength == sizeof(PH_HANDLE_TABLE_FLAGS_INFORMATION)) + { + flags = flagsInfo->Flags; + + if ((flags & PH_HANDLE_TABLE_VALID_FLAGS) == flags) + HandleTable->Flags = flags; + else + status = STATUS_INVALID_PARAMETER; + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + } + } + break; + default: + status = STATUS_INVALID_INFO_CLASS; + } + + return status; +} + PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( __inout PPH_HANDLE_TABLE HandleTable, __out PULONG HandleValue @@ -598,7 +717,7 @@ BOOLEAN PhpAllocateMoreHandleTableEntries( table1 = PhpCreateHandleTableLevel1(HandleTable); #ifdef PH_HANDLE_TABLE_SAFE - if (table1) + if (!table1) { PhpFreeHandleTableLevel2(table2); return FALSE; @@ -868,10 +987,10 @@ ULONG PhpMoveFreeHandleTableEntries( count < PH_HANDLE_TABLE_FREE_COUNT ) { - freeValueAlt = PH_HANDLE_VALUE_INVALID; + index = PH_HANDLE_VALUE_INVALID; } - return freeValueAlt; + return index; } PPH_HANDLE_TABLE_ENTRY PhpCreateHandleTableLevel0( diff --git a/2.x/trunk/phlib/include/handlep.h b/2.x/trunk/phlib/include/handlep.h index 2deb7528b..1c091679f 100644 --- a/2.x/trunk/phlib/include/handlep.h +++ b/2.x/trunk/phlib/include/handlep.h @@ -31,7 +31,7 @@ typedef struct _PH_HANDLE_TABLE { PH_QUEUED_LOCK Lock; - PH_QUEUED_LOCK LockedCondition; + PH_QUEUED_LOCK HandleWakeEvent; ULONG Count; ULONG_PTR TableValue; @@ -93,6 +93,11 @@ FORCEINLINE ULONG PhpDecodeHandle( return ((ULONG)Handle - PH_HANDLE_VALUE_BIAS) >> PH_HANDLE_VALUE_SHIFT; } +VOID PhpBlockOnLockedHandleTableEntry( + __inout PPH_HANDLE_TABLE HandleTable, + __in PPH_HANDLE_TABLE_ENTRY HandleTableEntry + ); + PPH_HANDLE_TABLE_ENTRY PhpAllocateHandleTableEntry( __inout PPH_HANDLE_TABLE HandleTable, __out PULONG HandleValue diff --git a/2.x/trunk/phlib/include/phbase.h b/2.x/trunk/phlib/include/phbase.h index 587bc27ad..58b844615 100644 --- a/2.x/trunk/phlib/include/phbase.h +++ b/2.x/trunk/phlib/include/phbase.h @@ -1662,6 +1662,7 @@ typedef struct _PH_HANDLE_TABLE_ENTRY #define PH_HANDLE_TABLE_FREE_COUNT 64 #define PH_HANDLE_TABLE_STRICT_FIFO 0x1 +#define PH_HANDLE_TABLE_VALID_FLAGS 0x1 VOID PhHandleTableInitialization(); @@ -1721,6 +1722,40 @@ VOID PhSweepHandleTable( __in PVOID Context ); +typedef enum _PH_HANDLE_TABLE_INFORMATION_CLASS +{ + HandleTableBasicInformation, + HandleTableFlagsInformation, + MaxHandleTableInfoClass +} PH_HANDLE_TABLE_INFORMATION_CLASS; + +typedef struct _PH_HANDLE_TABLE_BASIC_INFORMATION +{ + ULONG Count; + ULONG Flags; + ULONG TableLevel; +} PH_HANDLE_TABLE_BASIC_INFORMATION, *PPH_HANDLE_TABLE_BASIC_INFORMATION; + +typedef struct _PH_HANDLE_TABLE_FLAGS_INFORMATION +{ + ULONG Flags; +} PH_HANDLE_TABLE_FLAGS_INFORMATION, *PPH_HANDLE_TABLE_FLAGS_INFORMATION; + +NTSTATUS PhQueryInformationHandleTable( + __in PPH_HANDLE_TABLE HandleTable, + __in PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + __out_bcount_opt(BufferLength) PVOID Buffer, + __in ULONG BufferLength, + __out_opt PULONG ReturnLength + ); + +NTSTATUS PhSetInformationHandleTable( + __inout PPH_HANDLE_TABLE HandleTable, + __in PH_HANDLE_TABLE_INFORMATION_CLASS InformationClass, + __in_bcount(BufferLength) PVOID Buffer, + __in ULONG BufferLength + ); + // workqueue typedef struct _PH_WORK_QUEUE diff --git a/2.x/trunk/phlib/include/queuedlock.h b/2.x/trunk/phlib/include/queuedlock.h index 945da63fb..aaf239a1f 100644 --- a/2.x/trunk/phlib/include/queuedlock.h +++ b/2.x/trunk/phlib/include/queuedlock.h @@ -103,6 +103,24 @@ VOID FASTCALL PhfWaitForCondition( __in_opt PLARGE_INTEGER Timeout ); +#define PhQueueWakeEvent PhfQueueWakeEvent +VOID FASTCALL PhfQueueWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent, + __inout PPH_QUEUED_WAIT_BLOCK WaitBlock + ); + +#define PhSetWakeEvent PhfSetWakeEvent +VOID FASTCALL PhfSetWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent + ); + +#define PhWaitForWakeEvent PhfWaitForWakeEvent +NTSTATUS FASTCALL PhfWaitForWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent, + __inout PPH_QUEUED_WAIT_BLOCK WaitBlock, + __in_opt PLARGE_INTEGER Timeout + ); + // Inline functions FORCEINLINE VOID PhAcquireQueuedLockExclusiveFast( @@ -213,10 +231,18 @@ FORCEINLINE BOOLEAN PhTryAcquireReleaseQueuedLockExclusive( // compiler re-ordering the following check in either // direction. MemoryBarrier(); - owned = !!(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); + owned = !(QueuedLock->Value & PH_QUEUED_LOCK_OWNED); MemoryBarrier(); return owned; } +FORCEINLINE VOID PhSetWakeEventFast( + __inout PPH_QUEUED_LOCK WakeEvent + ) +{ + if (WakeEvent->Value) + PhSetWakeEvent(WakeEvent); +} + #endif diff --git a/2.x/trunk/phlib/queuedlock.c b/2.x/trunk/phlib/queuedlock.c index daee366e9..beb7ad675 100644 --- a/2.x/trunk/phlib/queuedlock.c +++ b/2.x/trunk/phlib/queuedlock.c @@ -286,8 +286,9 @@ FORCEINLINE PPH_QUEUED_WAIT_BLOCK PhpFindLastQueuedWaitBlock( * * \param WaitBlock A wait block. */ -__mayRaise FORCEINLINE VOID PhpBlockOnQueuedWaitBlock( - __inout PPH_QUEUED_WAIT_BLOCK WaitBlock +__mayRaise FORCEINLINE NTSTATUS PhpBlockOnQueuedWaitBlock( + __inout PPH_QUEUED_WAIT_BLOCK WaitBlock, + __in_opt PLARGE_INTEGER Timeout ) { NTSTATUS status; @@ -309,10 +310,16 @@ __mayRaise FORCEINLINE VOID PhpBlockOnQueuedWaitBlock( PhQueuedLockKeyedEventHandle, WaitBlock, FALSE, - NULL + Timeout ))) PhRaiseStatus(status); } + else + { + status = STATUS_SUCCESS; + } + + return status; } /** @@ -683,7 +690,7 @@ VOID FASTCALL PhfAcquireQueuedLockExclusive( if (optimize) PhpfOptimizeQueuedLockList(QueuedLock, currentValue); - PhpBlockOnQueuedWaitBlock(&waitBlock); + PhpBlockOnQueuedWaitBlock(&waitBlock, NULL); } } @@ -744,7 +751,7 @@ VOID FASTCALL PhfAcquireQueuedLockShared( if (optimize) PhpfOptimizeQueuedLockList(QueuedLock, currentValue); - PhpBlockOnQueuedWaitBlock(&waitBlock); + PhpBlockOnQueuedWaitBlock(&waitBlock, NULL); } } @@ -930,24 +937,32 @@ VOID FASTCALL PhfTryWakePushLock( * Wakes one thread sleeping on a condition variable. * * \param Condition A condition variable. + * + * \remarks The associated lock must be acquired before calling + * the function. */ VOID FASTCALL PhfPulseCondition( __inout PPH_QUEUED_LOCK Condition ) { - PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, FALSE); + if (Condition->Value & PH_QUEUED_LOCK_WAITERS) + PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, FALSE); } /** * Wakes all threads sleeping on a condition variable. * * \param Condition A condition variable. + * + * \remarks The associated lock must be acquired before calling + * the function. */ VOID FASTCALL PhfPulseAllCondition( __inout PPH_QUEUED_LOCK Condition ) { - PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, TRUE); + if (Condition->Value & PH_QUEUED_LOCK_WAITERS) + PhpfWakeQueuedLockEx(Condition, Condition->Value, TRUE, TRUE); } /** @@ -956,6 +971,9 @@ VOID FASTCALL PhfPulseAllCondition( * \param Condition A condition variable. * \param Lock A queued lock to release/acquire. * \param Timeout Not implemented. + * + * \remarks The associated lock must be acquired before calling + * the function. */ VOID FASTCALL PhfWaitForCondition( __inout PPH_QUEUED_LOCK Condition, @@ -992,7 +1010,7 @@ VOID FASTCALL PhfWaitForCondition( PhReleaseQueuedLockExclusiveFast(Lock); } - PhpBlockOnQueuedWaitBlock(&waitBlock); + PhpBlockOnQueuedWaitBlock(&waitBlock, NULL); if (Lock) { @@ -1005,3 +1023,94 @@ VOID FASTCALL PhfWaitForCondition( } } } + +/** + * Queues a wait block to a wake event. + * + * \param WakeEvent A wake event. + * \param WaitBlock A wait block. + * + * \remarks If you later determine that the wait should + * not occur, you must call PhfSetWakeEvent() to dequeue + * the wait block. + */ +VOID FASTCALL PhfQueueWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent, + __inout PPH_QUEUED_WAIT_BLOCK WaitBlock + ) +{ + PPH_QUEUED_WAIT_BLOCK value; + PPH_QUEUED_WAIT_BLOCK newValue; + + WaitBlock->Flags = PH_QUEUED_WAITER_SPINNING; + + value = (PPH_QUEUED_WAIT_BLOCK)WakeEvent->Value; + + while (TRUE) + { + WaitBlock->Next = value; + + if ((newValue = _InterlockedCompareExchangePointer( + (PPVOID)&WakeEvent->Value, + WaitBlock, + value + )) == value) + break; + + value = newValue; + } +} + +/** + * Sets a wake event, unblocking all queued wait blocks. + * + * \param WakeEvent A wake event. + */ +VOID FASTCALL PhfSetWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent + ) +{ + PPH_QUEUED_WAIT_BLOCK waitBlock; + + // Pop all waiters and unblock them. + + waitBlock = _InterlockedExchangePointer((PPVOID)&WakeEvent->Value, NULL); + + while (waitBlock) + { + waitBlock = waitBlock->Next; + PhpUnblockQueuedWaitBlock(waitBlock); + } +} + +/** + * Waits for a wake event to be set. + * + * \param WakeEvent A wake event. + * \param WaitBlock A wait block previously queued to + * the wake event using PhfQueueWakeEvent(). + * \param Timeout A timeout value. + * + * \param Wake events are subject to spurious wakeups. You + * should call this function in a loop which checks a + * predicate. + */ +NTSTATUS FASTCALL PhfWaitForWakeEvent( + __inout PPH_QUEUED_LOCK WakeEvent, + __inout PPH_QUEUED_WAIT_BLOCK WaitBlock, + __in_opt PLARGE_INTEGER Timeout + ) +{ + NTSTATUS status; + + status = PhpBlockOnQueuedWaitBlock(WaitBlock, Timeout); + + if (status != STATUS_SUCCESS) + { + // Probably a timeout. There's no way of unlinking + // the wait block safely, so just wake everyone. + PhSetWakeEvent(WakeEvent); + } + + return status; +}