[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:
Thomas Faber 2011-07-13 21:17:55 +00:00
parent f7dc696190
commit 270b3045e7
2 changed files with 254 additions and 6 deletions

View file

@ -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

View file

@ -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);