mirror of
https://github.com/reactos/reactos.git
synced 2025-05-30 22:49:12 +00:00
[KMTESTS/EX]
- ExResource part 2: test concurrent acquisition of a resource in multiple threads - ExInterlocked part 2: check calling convention correctness - these show quite a number of bugs in ReactOS ;) svn path=/branches/GSoC_2011/KMTestSuite/; revision=52674
This commit is contained in:
parent
f7dc696190
commit
270b3045e7
2 changed files with 254 additions and 6 deletions
|
@ -40,6 +40,36 @@ __declspec(dllimport) int __stdcall Exi386InterlockedDecrementLo
|
|||
|
||||
static KSPIN_LOCK SpinLock;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int esi, edi, ebx, ebp, esp;
|
||||
} PROCESSOR_STATE, *PPROCESSOR_STATE;
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_IX86)
|
||||
#define SaveState(State) do \
|
||||
{ \
|
||||
__asm lea ecx, [State] \
|
||||
__asm mov [ecx], esi \
|
||||
__asm mov [ecx+4], edi \
|
||||
__asm mov [ecx+8], ebx \
|
||||
__asm mov [ecx+12], ebp \
|
||||
__asm mov [ecx+16], esp \
|
||||
} while (0)
|
||||
|
||||
#define CheckState(OldState, NewState) do \
|
||||
{ \
|
||||
ok_eq_hex((OldState)->esi, (NewState)->esi); \
|
||||
ok_eq_hex((OldState)->edi, (NewState)->edi); \
|
||||
ok_eq_hex((OldState)->ebx, (NewState)->ebx); \
|
||||
ok_eq_hex((OldState)->ebp, (NewState)->ebp); \
|
||||
ok_eq_hex((OldState)->esp, (NewState)->esp); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define SaveState(State)
|
||||
#define CheckState(OldState, NewState)
|
||||
#endif
|
||||
|
||||
static
|
||||
LARGE_INTEGER
|
||||
Large(
|
||||
|
@ -57,7 +87,10 @@ Large(
|
|||
Type Value##Type = Val; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Ret##Type = Function(&Value##Type, Xchg, Cmp, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -75,8 +108,11 @@ Large(
|
|||
Type Exchange##Type = Xchg; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Ret##Type = Function(&Value##Type, &Exchange##Type, \
|
||||
&Compare##Type, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -94,7 +130,10 @@ Large(
|
|||
Type Value##Type = Val; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -110,7 +149,10 @@ Large(
|
|||
Type Value##Type = Val; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Ret##Type = Function(&Value##Type, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -126,7 +168,10 @@ Large(
|
|||
Type Value##Type = Val; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Ret##Type = Function(&Value##Type, Op, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -141,7 +186,10 @@ Large(
|
|||
Type Value##Type = Val; \
|
||||
Status = STATUS_SUCCESS; \
|
||||
_SEH2_TRY { \
|
||||
SaveState(OldState); \
|
||||
Function(&Value##Type, Op, ##__VA_ARGS__); \
|
||||
SaveState(NewState); \
|
||||
CheckState(&OldState, &NewState); \
|
||||
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \
|
||||
Status = _SEH2_GetExceptionCode(); \
|
||||
} _SEH2_END; \
|
||||
|
@ -163,6 +211,7 @@ TestInterlockedFunctional(VOID)
|
|||
{
|
||||
NTSTATUS Status;
|
||||
PKSPIN_LOCK pSpinLock = &SpinLock;
|
||||
PROCESSOR_STATE OldState, NewState;
|
||||
|
||||
/* on x86, most of these are supported intrinsicly and don't need a spinlock! */
|
||||
#if defined _M_IX86 || defined _M_AMD64
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
//#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
/* TODO: This is getting pretty long, make it somehow easier to read if possible */
|
||||
|
||||
/* TODO: this is the Windows Server 2003 version! ROS should use this!
|
||||
* This declaration can be removed once ROS headers are corrected */
|
||||
typedef struct _ERESOURCE_2K3 {
|
||||
|
@ -39,15 +41,15 @@ typedef struct _ERESOURCE_2K3 {
|
|||
KSPIN_LOCK SpinLock;
|
||||
} ERESOURCE_2K3, *PERESOURCE_2K3;
|
||||
|
||||
#define CheckResourceFields(Res) do \
|
||||
#define CheckResourceFields(Res, Reinit) do \
|
||||
{ \
|
||||
ok_eq_pointer((Res)->SystemResourcesList.Flink->Blink, &(Res)->SystemResourcesList); \
|
||||
ok_eq_pointer((Res)->SystemResourcesList.Blink->Flink, &(Res)->SystemResourcesList); \
|
||||
ok_eq_pointer((Res)->OwnerTable, NULL); \
|
||||
if (!Reinit) ok_eq_pointer((Res)->OwnerTable, NULL); \
|
||||
ok_eq_int((Res)->ActiveCount, 0); \
|
||||
ok_eq_uint((Res)->Flag, 0); \
|
||||
ok_eq_pointer((Res)->SharedWaiters, NULL); \
|
||||
ok_eq_pointer((Res)->ExclusiveWaiters, NULL); \
|
||||
if (!Reinit) ok_eq_pointer((Res)->SharedWaiters, NULL); \
|
||||
if (!Reinit) ok_eq_pointer((Res)->ExclusiveWaiters, NULL); \
|
||||
ok_eq_pointer((PVOID)(Res)->OwnerThreads[0].OwnerThread, NULL); \
|
||||
ok_eq_ulong((Res)->OwnerThreads[0].TableSize, 0LU); \
|
||||
ok_eq_pointer((PVOID)(Res)->OwnerThreads[1].OwnerThread, NULL); \
|
||||
|
@ -206,6 +208,201 @@ TestResourceUndocumentedShortcuts(
|
|||
CheckResourceStatus(Res, FALSE, Count, 0LU, 0LU);
|
||||
}
|
||||
|
||||
typedef BOOLEAN (NTAPI *PACQUIRE_FUNCTION)(PERESOURCE, BOOLEAN);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE Handle;
|
||||
PKTHREAD Thread;
|
||||
PERESOURCE Res;
|
||||
KEVENT InEvent;
|
||||
KEVENT OutEvent;
|
||||
PACQUIRE_FUNCTION AcquireResource;
|
||||
BOOLEAN Wait;
|
||||
BOOLEAN RetExpected;
|
||||
} THREAD_DATA, *PTHREAD_DATA;
|
||||
|
||||
static
|
||||
VOID
|
||||
NTAPI
|
||||
AcquireResourceThread(
|
||||
PVOID Context)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
PTHREAD_DATA ThreadData = Context;
|
||||
BOOLEAN Ret;
|
||||
|
||||
KeEnterCriticalRegion();
|
||||
Ret = ThreadData->AcquireResource(ThreadData->Res, ThreadData->Wait);
|
||||
if (ThreadData->RetExpected)
|
||||
ok_bool_true(Ret, "AcquireResource returned");
|
||||
else
|
||||
ok_bool_false(Ret, "AcquireResource returned");
|
||||
|
||||
ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
|
||||
Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
|
||||
if (Ret)
|
||||
ExReleaseResource(ThreadData->Res);
|
||||
KeLeaveCriticalRegion();
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
InitThreadData(
|
||||
PTHREAD_DATA ThreadData,
|
||||
PERESOURCE Res,
|
||||
PACQUIRE_FUNCTION AcquireFunction)
|
||||
{
|
||||
ThreadData->Res = Res;
|
||||
KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
|
||||
KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
|
||||
ThreadData->AcquireResource = AcquireFunction;
|
||||
}
|
||||
|
||||
static
|
||||
NTSTATUS
|
||||
StartThread(
|
||||
PTHREAD_DATA ThreadData,
|
||||
PLARGE_INTEGER Timeout,
|
||||
BOOLEAN Wait,
|
||||
BOOLEAN RetExpected)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
OBJECT_ATTRIBUTES Attributes;
|
||||
|
||||
ThreadData->Wait = Wait;
|
||||
ThreadData->RetExpected = RetExpected;
|
||||
InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireResourceThread, ThreadData);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
|
||||
return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
FinishThread(
|
||||
PTHREAD_DATA ThreadData)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
KeSetEvent(&ThreadData->InEvent, 0, TRUE);
|
||||
Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
|
||||
ObDereferenceObject(ThreadData->Thread);
|
||||
Status = ZwClose(ThreadData->Handle);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
KeClearEvent(&ThreadData->InEvent);
|
||||
KeClearEvent(&ThreadData->OutEvent);
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
TestResourceWithThreads(
|
||||
IN PERESOURCE Res)
|
||||
{
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
THREAD_DATA ThreadDataShared;
|
||||
THREAD_DATA ThreadDataShared2;
|
||||
THREAD_DATA ThreadDataExclusive;
|
||||
THREAD_DATA ThreadDataSharedStarve;
|
||||
THREAD_DATA ThreadDataSharedWait;
|
||||
LARGE_INTEGER Timeout;
|
||||
Timeout.QuadPart = -10 * 1000 * 10; /* 10 ms */
|
||||
|
||||
InitThreadData(&ThreadDataShared, Res, ExAcquireResourceSharedLite);
|
||||
InitThreadData(&ThreadDataShared2, Res, ExAcquireResourceSharedLite);
|
||||
InitThreadData(&ThreadDataExclusive, Res, ExAcquireResourceExclusiveLite);
|
||||
InitThreadData(&ThreadDataSharedStarve, Res, ExAcquireSharedStarveExclusive);
|
||||
InitThreadData(&ThreadDataSharedWait, Res, ExAcquireSharedWaitForExclusive);
|
||||
|
||||
/* have a thread acquire the resource shared */
|
||||
Status = StartThread(&ThreadDataShared, NULL, FALSE, TRUE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* a second thread should be able to acquire the resource shared */
|
||||
Status = StartThread(&ThreadDataShared2, NULL, FALSE, TRUE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 2);
|
||||
FinishThread(&ThreadDataShared2);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* now have a thread that tries to acquire the resource exclusive -- it should fail */
|
||||
Status = StartThread(&ThreadDataExclusive, NULL, FALSE, FALSE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
FinishThread(&ThreadDataExclusive);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* as above, but this time it should block */
|
||||
Status = StartThread(&ThreadDataExclusive, &Timeout, TRUE, TRUE);
|
||||
ok_eq_hex(Status, STATUS_TIMEOUT);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* now try another shared one -- it should fail */
|
||||
Status = StartThread(&ThreadDataShared2, NULL, FALSE, FALSE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
FinishThread(&ThreadDataShared2);
|
||||
|
||||
/* same for ExAcquireSharedWaitForExclusive */
|
||||
Status = StartThread(&ThreadDataSharedWait, NULL, FALSE, FALSE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
FinishThread(&ThreadDataSharedWait);
|
||||
|
||||
/* ExAcquireSharedStarveExclusive must get access though! */
|
||||
Status = StartThread(&ThreadDataSharedStarve, NULL, TRUE, TRUE);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 2);
|
||||
FinishThread(&ThreadDataSharedStarve);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* block another shared one */
|
||||
Status = StartThread(&ThreadDataShared2, &Timeout, TRUE, TRUE);
|
||||
ok_eq_hex(Status, STATUS_TIMEOUT);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 1LU, 1LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
|
||||
/* finish the very first one */
|
||||
FinishThread(&ThreadDataShared);
|
||||
|
||||
/* now the blocked exclusive one should get the resource */
|
||||
Status = KeWaitForSingleObject(&ThreadDataExclusive.OutEvent, Executive, KernelMode, FALSE, NULL);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 1LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
ok_eq_uint((Res->Flag & ResourceOwnedExclusive) != 0, 1);
|
||||
|
||||
FinishThread(&ThreadDataExclusive);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
|
||||
/* now the blocked shared one should resume */
|
||||
Status = KeWaitForSingleObject(&ThreadDataShared2.OutEvent, Executive, KernelMode, FALSE, NULL);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 1);
|
||||
FinishThread(&ThreadDataShared2);
|
||||
CheckResourceStatus(Res, FALSE, 0LU, 0LU, 0LU);
|
||||
ok_eq_int(Res->ActiveCount, 0);
|
||||
}
|
||||
|
||||
START_TEST(ExResource)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
@ -228,7 +425,7 @@ START_TEST(ExResource)
|
|||
memset(&Res, 0x55, sizeof Res);
|
||||
Status = ExInitializeResourceLite(&Res);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceFields((PERESOURCE_2K3)&Res);
|
||||
CheckResourceFields((PERESOURCE_2K3)&Res, FALSE);
|
||||
|
||||
CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
|
||||
|
||||
|
@ -246,10 +443,12 @@ START_TEST(ExResource)
|
|||
ok_bool_false(KeAreApcsDisabled(), "KeAreApcsDisabled returned");
|
||||
CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
|
||||
|
||||
TestResourceWithThreads(&Res);
|
||||
|
||||
/* ExReinitializeResourceLite cleans up after us */
|
||||
Status = ExReinitializeResourceLite(&Res);
|
||||
ok_eq_hex(Status, STATUS_SUCCESS);
|
||||
CheckResourceFields((PERESOURCE_2K3)&Res);
|
||||
CheckResourceFields((PERESOURCE_2K3)&Res, TRUE);
|
||||
CheckResourceStatus(&Res, FALSE, 0LU, 0LU, 0LU);
|
||||
|
||||
Status = ExDeleteResourceLite(&Res);
|
||||
|
|
Loading…
Reference in a new issue