From 270b3045e7e70a5e20d7c075dc9fa2ec2de30556 Mon Sep 17 00:00:00 2001 From: Thomas Faber Date: Wed, 13 Jul 2011 21:17:55 +0000 Subject: [PATCH] [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 --- kmtests/ntos_ex/ExInterlocked.c | 49 ++++++++ kmtests/ntos_ex/ExResource.c | 211 +++++++++++++++++++++++++++++++- 2 files changed, 254 insertions(+), 6 deletions(-) diff --git a/kmtests/ntos_ex/ExInterlocked.c b/kmtests/ntos_ex/ExInterlocked.c index d5dfebd9cf0..7de1b517d05 100644 --- a/kmtests/ntos_ex/ExInterlocked.c +++ b/kmtests/ntos_ex/ExInterlocked.c @@ -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 diff --git a/kmtests/ntos_ex/ExResource.c b/kmtests/ntos_ex/ExResource.c index 3040a71ca0a..cc017cb53c5 100644 --- a/kmtests/ntos_ex/ExResource.c +++ b/kmtests/ntos_ex/ExResource.c @@ -16,6 +16,8 @@ //#define NDEBUG #include +/* 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);