* 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
This commit is contained in:
wj32
2010-03-25 06:37:04 +00:00
parent 172af2dff9
commit 1aa2d22f86
5 changed files with 314 additions and 20 deletions
+129 -10
View File
@@ -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(
+6 -1
View File
@@ -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
+35
View File
@@ -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
+27 -1
View File
@@ -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
+117 -8
View File
@@ -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;
}