summaryrefslogtreecommitdiff
path: root/src/pal
diff options
context:
space:
mode:
authorKoundinya Veluri <kouvel@users.noreply.github.com>2018-02-14 10:27:32 -0800
committerGitHub <noreply@github.com>2018-02-14 10:27:32 -0800
commit3f20bea666d3662ae833b621a4b00bf034239f20 (patch)
treec1381bc4fbe621bccce32fb2e1f5842358cda7ca /src/pal
parent3dd6cf1dffb4a67c797d566c1d04daef41ef35f4 (diff)
downloadcoreclr-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.h9
-rw-r--r--src/pal/inc/pal_error.h1
-rw-r--r--src/pal/src/include/pal/synchobjects.hpp9
-rw-r--r--src/pal/src/synchmgr/wait.cpp157
-rw-r--r--src/pal/src/synchobj/semaphore.cpp4
-rw-r--r--src/pal/tests/palsuite/threading/CMakeLists.txt1
-rw-r--r--src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt17
-rw-r--r--src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp414
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;
+}