summaryrefslogtreecommitdiff
path: root/src/pal/tests
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/tests
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/tests')
-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
3 files changed, 432 insertions, 0 deletions
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;
+}