diff options
author | Koundinya Veluri <kouvel@users.noreply.github.com> | 2018-02-14 10:27:32 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-14 10:27:32 -0800 |
commit | 3f20bea666d3662ae833b621a4b00bf034239f20 (patch) | |
tree | c1381bc4fbe621bccce32fb2e1f5842358cda7ca /src/pal | |
parent | 3dd6cf1dffb4a67c797d566c1d04daef41ef35f4 (diff) | |
download | coreclr-3f20bea666d3662ae833b621a4b00bf034239f20.tar.gz coreclr-3f20bea666d3662ae833b621a4b00bf034239f20.tar.bz2 coreclr-3f20bea666d3662ae833b621a4b00bf034239f20.zip |
Implement WaitHandle.SignalAndWait on Unix (#16383)
Part of fix for https://github.com/dotnet/coreclr/issues/10441
Diffstat (limited to 'src/pal')
-rw-r--r-- | src/pal/inc/pal.h | 9 | ||||
-rw-r--r-- | src/pal/inc/pal_error.h | 1 | ||||
-rw-r--r-- | src/pal/src/include/pal/synchobjects.hpp | 9 | ||||
-rw-r--r-- | src/pal/src/synchmgr/wait.cpp | 157 | ||||
-rw-r--r-- | src/pal/src/synchobj/semaphore.cpp | 4 | ||||
-rw-r--r-- | src/pal/tests/palsuite/threading/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt | 17 | ||||
-rw-r--r-- | src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp | 414 |
8 files changed, 607 insertions, 5 deletions
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index 75a68956a1..6585435bbb 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -1494,6 +1494,15 @@ WaitForMultipleObjectsEx( IN DWORD dwMilliseconds, IN BOOL bAlertable); +PALIMPORT +DWORD +PALAPI +SignalObjectAndWait( + IN HANDLE hObjectToSignal, + IN HANDLE hObjectToWaitOn, + IN DWORD dwMilliseconds, + IN BOOL bAlertable); + #define DUPLICATE_CLOSE_SOURCE 0x00000001 #define DUPLICATE_SAME_ACCESS 0x00000002 diff --git a/src/pal/inc/pal_error.h b/src/pal/inc/pal_error.h index eb35d91419..f117298979 100644 --- a/src/pal/inc/pal_error.h +++ b/src/pal/inc/pal_error.h @@ -87,6 +87,7 @@ #define ERROR_NO_MORE_ITEMS 259L #define ERROR_DIRECTORY 267L #define ERROR_NOT_OWNER 288L +#define ERROR_TOO_MANY_POSTS 298L #define ERROR_PARTIAL_COPY 299L #define ERROR_INVALID_ADDRESS 487L #define ERROR_ARITHMETIC_OVERFLOW 534L diff --git a/src/pal/src/include/pal/synchobjects.hpp b/src/pal/src/include/pal/synchobjects.hpp index 1ee4f1c57b..06758cf8f9 100644 --- a/src/pal/src/include/pal/synchobjects.hpp +++ b/src/pal/src/include/pal/synchobjects.hpp @@ -42,7 +42,14 @@ namespace CorUnix DWORD dwMilliseconds, BOOL bAlertable, BOOL bPrioritize = FALSE); - + + DWORD InternalSignalObjectAndWait( + CPalThread *thread, + HANDLE hObjectToSignal, + HANDLE hObjectToWaitOn, + DWORD dwMilliseconds, + BOOL bAlertable); + PAL_ERROR InternalSleepEx( CPalThread * pthrCurrent, DWORD dwMilliseconds, diff --git a/src/pal/src/synchmgr/wait.cpp b/src/pal/src/synchmgr/wait.cpp index fc5bb674db..31153ae02b 100644 --- a/src/pal/src/synchmgr/wait.cpp +++ b/src/pal/src/synchmgr/wait.cpp @@ -23,6 +23,10 @@ Revision History: #include "pal/thread.hpp" #include "pal/synchobjects.hpp" +#include "pal/handlemgr.hpp" +#include "pal/event.hpp" +#include "pal/mutex.hpp" +#include "pal/semaphore.hpp" #include "pal/malloc.hpp" #include "pal/dbgmsg.h" @@ -45,6 +49,16 @@ static PalObjectTypeId sg_rgWaitObjectsIds[] = static CAllowedObjectTypes sg_aotWaitObject(sg_rgWaitObjectsIds, sizeof(sg_rgWaitObjectsIds)/sizeof(sg_rgWaitObjectsIds[0])); +static PalObjectTypeId sg_rgSignalableObjectIds[] = +{ + otiAutoResetEvent, + otiManualResetEvent, + otiMutex, + otiNamedMutex, + otiSemaphore +}; +static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, _countof(sg_rgSignalableObjectIds)); + /*++ Function: WaitForSingleObject @@ -180,8 +194,8 @@ WaitForMultipleObjectsEx(IN DWORD nCount, PERF_ENTRY(WaitForMultipleObjectsEx); ENTRY("WaitForMultipleObjectsEx(nCount=%d, lpHandles=%p," - " bWaitAll=%d, dwMilliseconds=%u, bAlertable=d)\n", - nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable); + " bWaitAll=%d, dwMilliseconds=%u, bAlertable=%s)\n", + nCount, lpHandles, bWaitAll, dwMilliseconds, bAlertable ? "TRUE" : "FALSE"); CPalThread * pThread = InternalGetCurrentThread(); @@ -195,6 +209,36 @@ WaitForMultipleObjectsEx(IN DWORD nCount, /*++ Function: + SignalObjectAndWait + +See MSDN doc for info about this function. +--*/ +DWORD +PALAPI +SignalObjectAndWait( + IN HANDLE hObjectToSignal, + IN HANDLE hObjectToWaitOn, + IN DWORD dwMilliseconds, + IN BOOL bAlertable) +{ + PERF_ENTRY(SignalObjectAndWait); + ENTRY( + "SignalObjectAndWait(hObjectToSignal=%p, hObjectToWaitOn=%p, dwMilliseconds=%u, bAlertable=%s)\n", + hObjectToSignal, + hObjectToWaitOn, + dwMilliseconds, + bAlertable ? "TRUE" : "FALSE"); + + CPalThread *thread = InternalGetCurrentThread(); + DWORD result = InternalSignalObjectAndWait(thread, hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); + + LOGEXIT("SignalObjectAndWait returns DWORD %u\n", result); + PERF_EXIT(SignalObjectAndWait); + return result; +} + +/*++ +Function: Sleep See MSDN doc. @@ -669,6 +713,115 @@ WFMOExIntExit: return dwRet; } +DWORD CorUnix::InternalSignalObjectAndWait( + CPalThread *thread, + HANDLE hObjectToSignal, + HANDLE hObjectToWaitOn, + DWORD dwMilliseconds, + BOOL bAlertable) +{ + DWORD result = WAIT_FAILED; + PAL_ERROR palError = NO_ERROR; + IPalObject *objectToSignal = nullptr; + IPalObject *objectToWaitOn = nullptr; + + // Validate and add a reference to the object to signal + palError = + g_pObjectManager->ReferenceObjectByHandle( + thread, + hObjectToSignal, + &sg_aotSignalableObject, + 0, // should be MUTEX_MODIFY_STATE or equivalent for a signalable object, currently ignored (no Win32 security) + &objectToSignal); + if (palError != NO_ERROR) + { + ERROR("Unable to obtain object for handle %p (error %u)!\n", hObjectToSignal, palError); + goto InternalSignalObjectAndWait_Error; + } + + // Validate and add a reference to the object to wait on. Error checking is done before signaling. + palError = + g_pObjectManager->ReferenceObjectByHandle( + thread, + hObjectToWaitOn, + &sg_aotWaitObject, + SYNCHRONIZE, + &objectToWaitOn); + if (palError != NO_ERROR) + { + ERROR("Unable to obtain object for handle %p (error %u)!\n", hObjectToWaitOn, palError); + goto InternalSignalObjectAndWait_Error; + } + + // Signal + switch (objectToSignal->GetObjectType()->GetId()) + { + case otiAutoResetEvent: + case otiManualResetEvent: + palError = InternalSetEvent(thread, hObjectToSignal, true /* fSetEvent */); + break; + + case otiMutex: + case otiNamedMutex: + palError = InternalReleaseMutex(thread, hObjectToSignal); + break; + + case otiSemaphore: + palError = InternalReleaseSemaphore(thread, hObjectToSignal, 1 /* lReleaseCount */, nullptr /* lpPreviousCount */); + break; + + default: + palError = ERROR_INVALID_HANDLE; + break; + } + if (palError != NO_ERROR) + { + ERROR("Unable to signal object for handle %p (error %u)!\n", hObjectToSignal, palError); + goto InternalSignalObjectAndWait_Error; + } + objectToSignal->ReleaseReference(thread); + objectToSignal = nullptr; + + // Wait + result = + InternalWaitForMultipleObjectsEx( + thread, + 1 /* nCount */, + &hObjectToWaitOn, + false /* bWaitAll */, + dwMilliseconds, + bAlertable); + if (result == WAIT_FAILED) + { + ERROR("Unable to wait on object for handle %p (error %u)!\n", hObjectToWaitOn, palError); + goto InternalSignalObjectAndWait_Error; + } + objectToWaitOn->ReleaseReference(thread); + objectToWaitOn = nullptr; + + goto InternalSignalObjectAndWait_Exit; + +InternalSignalObjectAndWait_Error: + if (objectToSignal != nullptr) + { + objectToSignal->ReleaseReference(thread); + } + if (objectToWaitOn != nullptr) + { + objectToWaitOn->ReleaseReference(thread); + } + + if (palError != NO_ERROR) + { + _ASSERTE(result == WAIT_FAILED); + thread->SetLastError(palError); + } + +InternalSignalObjectAndWait_Exit: + LOGEXIT("InternalSignalObjectAndWait returns %u\n", result); + return result; +} + DWORD CorUnix::InternalSleepEx ( CPalThread * pThread, DWORD dwMilliseconds, diff --git a/src/pal/src/synchobj/semaphore.cpp b/src/pal/src/synchobj/semaphore.cpp index d11003ff5c..499f847978 100644 --- a/src/pal/src/synchobj/semaphore.cpp +++ b/src/pal/src/synchobj/semaphore.cpp @@ -510,7 +510,7 @@ CorUnix::InternalReleaseSemaphore( _ASSERTE(lOldCount <= pSemaphoreData->lMaximumCount); if (lReleaseCount > pSemaphoreData->lMaximumCount - lOldCount) { - palError = ERROR_INVALID_PARAMETER; + palError = ERROR_TOO_MANY_POSTS; goto InternalReleaseSemaphoreExit; } @@ -596,4 +596,4 @@ OpenSemaphoreW( PERF_EXIT(OpenSemaphoreW); return hSemaphore; -}
\ No newline at end of file +} diff --git a/src/pal/tests/palsuite/threading/CMakeLists.txt b/src/pal/tests/palsuite/threading/CMakeLists.txt index bd31d17579..32e66b7518 100644 --- a/src/pal/tests/palsuite/threading/CMakeLists.txt +++ b/src/pal/tests/palsuite/threading/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(ResetEvent) add_subdirectory(ResumeThread) add_subdirectory(SetErrorMode) add_subdirectory(SetEvent) +add_subdirectory(SignalObjectAndWait) add_subdirectory(Sleep) add_subdirectory(SleepEx) add_subdirectory(SwitchToThread) diff --git a/src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt b/src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt new file mode 100644 index 0000000000..464386c5b5 --- /dev/null +++ b/src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8.12.2) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(SOURCES + SignalObjectAndWaitTest.cpp +) + +add_executable(paltest_signalobjectandwaittest + ${SOURCES} +) + +add_dependencies(paltest_signalobjectandwaittest coreclrpal) + +target_link_libraries(paltest_signalobjectandwaittest + ${COMMON_TEST_LIBRARIES} +) diff --git a/src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp b/src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp new file mode 100644 index 0000000000..9ec1ed3dc8 --- /dev/null +++ b/src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp @@ -0,0 +1,414 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <palsuite.h> + +enum class SignalableObjectType +{ + First = 0, + + Invalid = First, + ManualResetEvent, + AutoResetEvent, + Semaphore, + FullSemaphore, + Mutex, + UnlockedMutex, + + Last = UnlockedMutex +}; + +enum class WaitableObjectType +{ + First = 0, + + Invalid = First, + ManualResetEvent, + UnsignaledManualResetEvent, + AutoResetEvent, + UnsignaledAutoResetEvent, + Semaphore, + EmptySemaphore, + Mutex, + LockedMutex, + + Last = LockedMutex +}; + +void operator ++(SignalableObjectType &objectType) +{ + ++(int &)objectType; +} + +void operator ++(WaitableObjectType &objectType) +{ + ++(int &)objectType; +} + +struct AssertionFailureException +{ + const int lineNumber; + const char *const expression; + SignalableObjectType signalableObjectType; + WaitableObjectType waitableObjectType; + DWORD waitResult; + DWORD errorCode; + + AssertionFailureException(int lineNumber, const char *expression) + : lineNumber(lineNumber), + expression(expression), + signalableObjectType(SignalableObjectType::Invalid), + waitableObjectType(WaitableObjectType::Invalid), + waitResult(WAIT_OBJECT_0), + errorCode(ERROR_SUCCESS) + { + } +}; + +#define TestAssert(expression) \ + do \ + { \ + if (!(expression)) \ + { \ + throw AssertionFailureException(__LINE__, "" #expression ""); \ + } \ + } while (false) + +HANDLE CreateObjectToSignal(SignalableObjectType objectType) +{ + switch (objectType) + { + case SignalableObjectType::Invalid: + return nullptr; + + case SignalableObjectType::ManualResetEvent: + return CreateEvent(nullptr, true, false, nullptr); + + case SignalableObjectType::AutoResetEvent: + return CreateEvent(nullptr, false, false, nullptr); + + case SignalableObjectType::Semaphore: + return CreateSemaphore(nullptr, 0, 1, nullptr); + + case SignalableObjectType::FullSemaphore: + return CreateSemaphore(nullptr, 1, 1, nullptr); + + case SignalableObjectType::Mutex: + return CreateMutex(nullptr, true, nullptr); + + case SignalableObjectType::UnlockedMutex: + return CreateMutex(nullptr, false, nullptr); + + default: + TestAssert(false); + } +} + +void VerifySignal(HANDLE h, SignalableObjectType objectType) +{ + switch (objectType) + { + case SignalableObjectType::ManualResetEvent: + TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); + break; + + case SignalableObjectType::AutoResetEvent: + TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); + SetEvent(h); + break; + + case SignalableObjectType::Semaphore: + TestAssert(!ReleaseSemaphore(h, 1, nullptr)); + break; + + case SignalableObjectType::Mutex: + TestAssert(!ReleaseMutex(h)); + break; + + default: + TestAssert(false); + } +} + +void CloseObjectToSignal(HANDLE h, SignalableObjectType objectType) +{ + if (objectType != SignalableObjectType::Invalid) + { + CloseHandle(h); + } +} + +HANDLE CreateObjectToWaitOn(WaitableObjectType objectType) +{ + switch (objectType) + { + case WaitableObjectType::Invalid: + return nullptr; + + case WaitableObjectType::ManualResetEvent: + return CreateEvent(nullptr, true, true, nullptr); + + case WaitableObjectType::UnsignaledManualResetEvent: + return CreateEvent(nullptr, true, false, nullptr); + + case WaitableObjectType::AutoResetEvent: + return CreateEvent(nullptr, false, true, nullptr); + + case WaitableObjectType::UnsignaledAutoResetEvent: + return CreateEvent(nullptr, false, false, nullptr); + + case WaitableObjectType::Semaphore: + return CreateSemaphore(nullptr, 1, 1, nullptr); + + case WaitableObjectType::EmptySemaphore: + return CreateSemaphore(nullptr, 0, 1, nullptr); + + case WaitableObjectType::Mutex: + return CreateMutex(nullptr, false, nullptr); + + case WaitableObjectType::LockedMutex: + return CreateMutex(nullptr, true, nullptr); + + default: + TestAssert(false); + } +} + +void VerifyWait(HANDLE h, WaitableObjectType objectType) +{ + switch (objectType) + { + case WaitableObjectType::ManualResetEvent: + case WaitableObjectType::UnsignaledManualResetEvent: + break; + + case WaitableObjectType::AutoResetEvent: + case WaitableObjectType::UnsignaledAutoResetEvent: + case WaitableObjectType::Semaphore: + case WaitableObjectType::EmptySemaphore: + TestAssert(WaitForSingleObject(h, 0) == WAIT_TIMEOUT); + break; + + case WaitableObjectType::Mutex: + TestAssert(ReleaseMutex(h)); + TestAssert(!ReleaseMutex(h)); + TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); + break; + + case WaitableObjectType::LockedMutex: + TestAssert(ReleaseMutex(h)); + TestAssert(ReleaseMutex(h)); + TestAssert(!ReleaseMutex(h)); + TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); + TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); + break; + + default: + TestAssert(false); + } +} + +void CloseObjectToWaitOn(HANDLE h, WaitableObjectType objectType) +{ + switch (objectType) + { + case WaitableObjectType::ManualResetEvent: + case WaitableObjectType::UnsignaledManualResetEvent: + case WaitableObjectType::AutoResetEvent: + case WaitableObjectType::UnsignaledAutoResetEvent: + CloseHandle(h); + break; + + case WaitableObjectType::Semaphore: + case WaitableObjectType::EmptySemaphore: + ReleaseSemaphore(h, 1, nullptr); + CloseHandle(h); + break; + + case WaitableObjectType::Mutex: + ReleaseMutex(h); + CloseHandle(h); + break; + + case WaitableObjectType::LockedMutex: + ReleaseMutex(h); + ReleaseMutex(h); + CloseHandle(h); + break; + + default: + break; + } +} + +bool Verify(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType, DWORD waitResult, DWORD errorCode) +{ + if (signalableObjectType == SignalableObjectType::Invalid || waitableObjectType == WaitableObjectType::Invalid) + { + TestAssert(waitResult == WAIT_FAILED); + TestAssert(errorCode == ERROR_INVALID_HANDLE); + return false; + } + + switch (signalableObjectType) + { + case SignalableObjectType::FullSemaphore: + TestAssert(waitResult == WAIT_FAILED); + TestAssert(errorCode == ERROR_TOO_MANY_POSTS); + return false; + + case SignalableObjectType::UnlockedMutex: + TestAssert(waitResult == WAIT_FAILED); + TestAssert(errorCode == ERROR_NOT_OWNER); + return false; + + default: + break; + } + + switch (waitableObjectType) + { + case WaitableObjectType::UnsignaledManualResetEvent: + case WaitableObjectType::UnsignaledAutoResetEvent: + case WaitableObjectType::EmptySemaphore: + TestAssert(waitResult == WAIT_TIMEOUT); + break; + + default: + TestAssert(waitResult == WAIT_OBJECT_0); + break; + } + TestAssert(errorCode == ERROR_SUCCESS); + return true; +} + +void Run(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType) +{ + HANDLE objectToSignal = CreateObjectToSignal(signalableObjectType); + TestAssert(signalableObjectType == SignalableObjectType::Invalid || objectToSignal != nullptr); + HANDLE objectToWaitOn = CreateObjectToWaitOn(waitableObjectType); + TestAssert(waitableObjectType == WaitableObjectType::Invalid || objectToWaitOn != nullptr); + DWORD waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); + DWORD errorCode = waitResult == WAIT_FAILED ? GetLastError() : ERROR_SUCCESS; + + try + { + if (Verify(signalableObjectType, waitableObjectType, waitResult, errorCode)) + { + VerifySignal(objectToSignal, signalableObjectType); + VerifyWait(objectToWaitOn, waitableObjectType); + } + } + catch (AssertionFailureException ex) + { + ex.signalableObjectType = signalableObjectType; + ex.waitableObjectType = waitableObjectType; + ex.waitResult = waitResult; + ex.errorCode = errorCode; + throw ex; + } +} + +static bool s_apcCalled = false; + +void CALLBACK ApcCallback(ULONG_PTR dwParam) +{ + s_apcCalled = true; + HANDLE *objects = (HANDLE *)dwParam; + HANDLE objectToSignal = objects[0]; + HANDLE objectToWaitOn = objects[1]; + TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred + TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet + SetEvent(objectToWaitOn); +} + +void Run() +{ + for (SignalableObjectType signalableObjectType = SignalableObjectType::First; + signalableObjectType <= SignalableObjectType::Last; + ++signalableObjectType) + { + for (WaitableObjectType waitableObjectType = WaitableObjectType::First; + waitableObjectType <= WaitableObjectType::Last; + ++waitableObjectType) + { + Run(signalableObjectType, waitableObjectType); + } + } + + DWORD waitResult = WAIT_FAILED; + try + { + HANDLE objectToSignal = CreateObjectToSignal(SignalableObjectType::ManualResetEvent); + TestAssert(objectToSignal != nullptr); + HANDLE objectToWaitOn = CreateObjectToWaitOn(WaitableObjectType::AutoResetEvent); + TestAssert(objectToWaitOn != nullptr); + HANDLE objects[] = {objectToSignal, objectToWaitOn}; + + // Verify that a queued APC is not called if the wait is not alertable + QueueUserAPC(&ApcCallback, GetCurrentThread(), (ULONG_PTR)&objects); + waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, false); + TestAssert(waitResult == WAIT_OBJECT_0); + TestAssert(!s_apcCalled); + TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred + TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred + + // Verify that signal, call APC, wait, occur in that order + ResetEvent(objectToSignal); + SetEvent(objectToWaitOn); + waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); + TestAssert(waitResult == WAIT_IO_COMPLETION); + TestAssert(s_apcCalled); + TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred + TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet + s_apcCalled = false; + ResetEvent(objectToSignal); + SetEvent(objectToWaitOn); + waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); + TestAssert(waitResult == WAIT_OBJECT_0); + TestAssert(!s_apcCalled); + TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred + TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred + + CloseHandle(objectToSignal); + CloseHandle(objectToWaitOn); + } + catch (AssertionFailureException ex) + { + ex.signalableObjectType = SignalableObjectType::ManualResetEvent; + ex.waitableObjectType = WaitableObjectType::AutoResetEvent; + ex.waitResult = waitResult; + throw ex; + } +} + +int _cdecl main(int argc, char **argv) +{ + if (PAL_Initialize(argc, argv) != 0) + { + return FAIL; + } + + int testReturnCode = PASS; + try + { + Run(); + } + catch (AssertionFailureException ex) + { + printf( + "SignalObjectAndWaitTest - Assertion failure (line %d, signalable object type %d, waitable object type %d, wait result 0x%x, error code %u): '%s'\n", + ex.lineNumber, + ex.signalableObjectType, + ex.waitableObjectType, + ex.waitResult, + ex.errorCode, + ex.expression); + fflush(stdout); + testReturnCode = FAIL; + } + + PAL_TerminateEx(testReturnCode); + return testReturnCode; +} |