Implement WaitHandle.SignalAndWait on Unix (#16383)
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Wed, 14 Feb 2018 18:27:32 +0000 (10:27 -0800)
committerGitHub <noreply@github.com>
Wed, 14 Feb 2018 18:27:32 +0000 (10:27 -0800)
Part of fix for https://github.com/dotnet/coreclr/issues/10441

14 files changed:
src/mscorlib/src/System/Threading/WaitHandle.cs
src/pal/inc/pal.h
src/pal/inc/pal_error.h
src/pal/src/include/pal/synchobjects.hpp
src/pal/src/synchmgr/wait.cpp
src/pal/src/synchobj/semaphore.cpp
src/pal/tests/palsuite/threading/CMakeLists.txt
src/pal/tests/palsuite/threading/SignalObjectAndWait/CMakeLists.txt [new file with mode: 0644]
src/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp [new file with mode: 0644]
src/vm/comwaithandle.cpp
src/vm/comwaithandle.h
src/vm/ecalllist.h
src/vm/threads.cpp
src/vm/threads.h

index 69c3445..3c3b89d 100644 (file)
@@ -421,21 +421,15 @@ namespace System.Threading
         ==  SignalAndWait
         ==
         ==================================================*/
-#if PLATFORM_WINDOWS
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal, SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout,
                                             bool hasThreadAffinity, bool exitContext);
-#endif // PLATFORM_WINDOWS       
 
         public static bool SignalAndWait(
                                         WaitHandle toSignal,
                                         WaitHandle toWaitOn)
         {
-#if PLATFORM_UNIX
-            throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441
-#else
             return SignalAndWait(toSignal, toWaitOn, -1, false);
-#endif
         }
 
         public static bool SignalAndWait(
@@ -444,16 +438,12 @@ namespace System.Threading
                                         TimeSpan timeout,
                                         bool exitContext)
         {
-#if PLATFORM_UNIX
-            throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441
-#else
             long tm = (long)timeout.TotalMilliseconds;
             if (-1 > tm || (long)Int32.MaxValue < tm)
             {
                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
             }
             return SignalAndWait(toSignal, toWaitOn, (int)tm, exitContext);
-#endif
         }
 
         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
@@ -463,9 +453,6 @@ namespace System.Threading
                                         int millisecondsTimeout,
                                         bool exitContext)
         {
-#if PLATFORM_UNIX
-            throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // https://github.com/dotnet/coreclr/issues/10441
-#else
             if (null == toSignal)
             {
                 throw new ArgumentNullException(nameof(toSignal));
@@ -501,7 +488,6 @@ namespace System.Threading
 
             //Timeout
             return false;
-#endif
         }
 
         private static void ThrowAbandonedMutexException()
index 75a6895..6585435 100644 (file)
@@ -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
 
index eb35d91..f117298 100644 (file)
@@ -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
index 1ee4f1c..06758cf 100644 (file)
@@ -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,
index fc5bb67..31153ae 100644 (file)
@@ -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,
index d11003f..499f847 100644 (file)
@@ -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
+}
index bd31d17..32e66b7 100644 (file)
@@ -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 (file)
index 0000000..464386c
--- /dev/null
@@ -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 (file)
index 0000000..9ec1ed3
--- /dev/null
@@ -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;
+}
index 50bff8b..7d0b7b7 100644 (file)
@@ -282,7 +282,6 @@ FCIMPL4(INT32, WaitHandleNative::CorWaitMultipleNative, Object* waitObjectsUNSAF
 }
 FCIMPLEND
 
-#ifndef FEATURE_PAL
 FCIMPL5(INT32, WaitHandleNative::CorSignalAndWaitOneNative, SafeHandle* safeWaitHandleSignalUNSAFE,SafeHandle* safeWaitHandleWaitUNSAFE, INT32 timeout, CLR_BOOL hasThreadAffinity, CLR_BOOL exitContext)
 {
     FCALL_CONTRACT;
@@ -335,4 +334,3 @@ FCIMPL5(INT32, WaitHandleNative::CorSignalAndWaitOneNative, SafeHandle* safeWait
     return retVal;
 }
 FCIMPLEND
-#endif // !FEATURE_PAL
index f593b7e..9c27460 100644 (file)
@@ -21,8 +21,6 @@ class WaitHandleNative
 public:
     static FCDECL4(INT32, CorWaitOneNative, SafeHandle* safeWaitHandleUNSAFE, INT32 timeout, CLR_BOOL hasThreadAffinity, CLR_BOOL exitContext);
     static FCDECL4(INT32, CorWaitMultipleNative, Object* waitObjectsUNSAFE, INT32 timeout, CLR_BOOL exitContext, CLR_BOOL waitForAll);
-#ifndef FEATURE_PAL
     static FCDECL5(INT32, CorSignalAndWaitOneNative, SafeHandle* safeWaitHandleSignalUNSAFE, SafeHandle* safeWaitHandleWaitUNSAFE, INT32 timeout, CLR_BOOL hasThreadAffinity, CLR_BOOL exitContext);
-#endif
 };
 #endif
index 851ab2f..86bc6a2 100644 (file)
@@ -744,9 +744,7 @@ FCFuncEnd()
 FCFuncStart(gWaitHandleFuncs)
     FCFuncElement("WaitOneNative", WaitHandleNative::CorWaitOneNative)
     FCFuncElement("WaitMultiple", WaitHandleNative::CorWaitMultipleNative)
-#ifndef FEATURE_PAL
     FCFuncElement("SignalAndWaitOne", WaitHandleNative::CorSignalAndWaitOneNative)
-#endif // !FEATURE_PAL
 FCFuncEnd()
 
 FCFuncStart(gNumberFuncs)
index 422789b..7bdc358 100644 (file)
@@ -3868,7 +3868,6 @@ WaitCompleted:
     return ret;
 }
 
-#ifndef FEATURE_PAL
 //--------------------------------------------------------------------
 // Only one style of wait for DoSignalAndWait since we don't support this on STA Threads
 //--------------------------------------------------------------------
@@ -4014,7 +4013,6 @@ WaitCompleted:
 
     return ret;
 }
-#endif // !FEATURE_PAL
 
 DWORD Thread::DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis)
 {
index d55d1f8..09bab42 100644 (file)
@@ -3291,19 +3291,15 @@ public:
 
     DWORD          DoAppropriateWait(AppropriateWaitFunc func, void *args, DWORD millis,
                                      WaitMode mode, PendingSync *syncInfo = 0);
-#ifndef FEATURE_PAL
     DWORD          DoSignalAndWait(HANDLE *handles, DWORD millis, BOOL alertable,
                                      PendingSync *syncState = 0);
-#endif // !FEATURE_PAL
 private:
     void           DoAppropriateWaitWorkerAlertableHelper(WaitMode mode);
     DWORD          DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL waitAll,
                                            DWORD millis, WaitMode mode);
     DWORD          DoAppropriateWaitWorker(AppropriateWaitFunc func, void *args,
                                            DWORD millis, WaitMode mode);
-#ifndef FEATURE_PAL
     DWORD          DoSignalAndWaitWorker(HANDLE* pHandles, DWORD millis,BOOL alertable);
-#endif // !FEATURE_PAL
     DWORD          DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, DWORD timeout, WaitMode mode);
     DWORD          DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis);
 public: