Author: tfaber Date: Wed Jul 13 21:17:55 2011 New Revision: 52674
URL: http://svn.reactos.org/svn/reactos?rev=52674&view=rev Log: [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 ;)
Modified: branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExInterlocked.c branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExResource.c
Modified: branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExInterlocked.c URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2011/KMTestSuite/kmtests/nt... ============================================================================== --- branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExInterlocked.c [iso-8859-1] (original) +++ branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExInterlocked.c [iso-8859-1] Wed Jul 13 21:17:55 2011 @@ -40,6 +40,36 @@
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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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
Modified: branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExResource.c URL: http://svn.reactos.org/svn/reactos/branches/GSoC_2011/KMTestSuite/kmtests/nt... ============================================================================== --- branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExResource.c [iso-8859-1] (original) +++ branches/GSoC_2011/KMTestSuite/kmtests/ntos_ex/ExResource.c [iso-8859-1] Wed Jul 13 21:17:55 2011 @@ -15,6 +15,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 */ @@ -39,15 +41,15 @@ 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 @@ 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 @@ 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 @@ 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);