From 7cca75c8e95f129a21c33f1f4568e90e9e397f9d Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Wed, 29 Jun 2022 10:28:53 -0500 Subject: Add AppWaitForSingleObject/MultipleObjects, ThreadWaitForCompletion. --- src/libs/dutil/WixToolset.DUtil/apputil.cpp | 68 ++++++++++++++++++++++ src/libs/dutil/WixToolset.DUtil/dutil.vcxproj | 2 + .../dutil/WixToolset.DUtil/dutil.vcxproj.filters | 6 ++ src/libs/dutil/WixToolset.DUtil/inc/apputil.h | 21 +++++++ src/libs/dutil/WixToolset.DUtil/inc/dutil.h | 2 + src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h | 1 + src/libs/dutil/WixToolset.DUtil/inc/procutil.h | 2 +- src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h | 22 +++++++ src/libs/dutil/WixToolset.DUtil/monutil.cpp | 34 +++++------ src/libs/dutil/WixToolset.DUtil/precomp.h | 1 + src/libs/dutil/WixToolset.DUtil/procutil.cpp | 42 ++++++------- src/libs/dutil/WixToolset.DUtil/thrdutil.cpp | 46 +++++++++++++++ src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp | 60 +++++++++++++++++++ .../dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 1 + .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 3 + src/libs/dutil/test/DUtilUnitTest/precomp.h | 1 + 16 files changed, 269 insertions(+), 43 deletions(-) create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/thrdutil.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp (limited to 'src/libs') diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp index 9e75082a..42f589dc 100644 --- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp @@ -313,6 +313,74 @@ LExit: return hr; } +DAPI_(HRESULT) AppWaitForSingleObject( + __in HANDLE hHandle, + __in DWORD dwMilliseconds + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + + dwResult = ::WaitForSingleObject(hHandle, dwMilliseconds); + if (WAIT_TIMEOUT == dwResult) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult)); + } + else if (WAIT_ABANDONED == dwResult) + { + AppExitOnWin32Error(dwResult, hr, "Abandoned wait for single object."); + } + else if (WAIT_OBJECT_0 != dwResult) + { + AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForSingleObject."); + AppExitWithLastError(hr, "Failed to wait for single object."); + } + +LExit: + return hr; +} + +DAPI_(HRESULT) AppWaitForMultipleObjects( + __in DWORD dwCount, + __in const HANDLE* rghHandles, + __in BOOL fWaitAll, + __in DWORD dwMilliseconds, + __out_opt DWORD* pdwSignaledIndex + ) +{ + HRESULT hr = S_OK; + DWORD dwResult = 0; + DWORD dwSignaledIndex = dwCount; + + dwResult = ::WaitForMultipleObjects(dwCount, rghHandles, fWaitAll, dwMilliseconds); + if (WAIT_TIMEOUT == dwResult) + { + ExitFunction1(hr = HRESULT_FROM_WIN32(dwResult)); + } + else if (WAIT_ABANDONED_0 <= dwResult && (WAIT_ABANDONED_0 + dwCount) > dwResult) + { + dwSignaledIndex = dwResult - WAIT_ABANDONED_0; + AppExitOnWin32Error(dwResult, hr, "Abandoned wait for multiple objects, index: %u.", dwSignaledIndex); + } + else if (WAIT_OBJECT_0 <= dwResult && (WAIT_OBJECT_0 + dwCount) > dwResult) + { + dwSignaledIndex = dwResult - WAIT_OBJECT_0; + } + else + { + AssertSz(WAIT_FAILED == dwResult, "Unexpected return code from WaitForMultipleObjects."); + AppExitWithLastError(hr, "Failed to wait for multiple objects."); + } + +LExit: + if (pdwSignaledIndex) + { + *pdwSignaledIndex = dwSignaledIndex; + } + + return hr; +} + static HRESULT EscapeCommandLineArgument( __in_z LPCWSTR wzArgument, __out_z LPWSTR* psczEscaped diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj index 3e3c42b7..c84b98fe 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj @@ -104,6 +104,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters index 07698f9e..efadef6f 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters @@ -162,6 +162,9 @@ Source Files + + Source Files + Source Files @@ -344,6 +347,9 @@ Header Files + + Header Files + Header Files diff --git a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h index 95a98e73..e2812ee4 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/apputil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/apputil.h @@ -84,6 +84,27 @@ HRESULT DAPI AppEscapeCommandLineArgumentFormattedArgs( __in va_list args ); +/******************************************************************** +AppWaitForSingleObject - wrapper for ::WaitForSingleObject. + +********************************************************************/ +HRESULT DAPI AppWaitForSingleObject( + __in HANDLE hHandle, + __in DWORD dwMilliseconds + ); + +/******************************************************************** +AppWaitForMultipleObjects - wrapper for ::WaitForMultipleObjects. + +********************************************************************/ +HRESULT DAPI AppWaitForMultipleObjects( + __in DWORD dwCount, + __in const HANDLE* rghHandles, + __in BOOL fWaitAll, + __in DWORD dwMilliseconds, + __out_opt DWORD* pdwSignaledIndex + ); + #ifdef __cplusplus } #endif diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h index 6f099f35..2db64812 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/dutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/dutil.h @@ -130,6 +130,7 @@ void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT h #define ExitOnWin32ErrorSource(d, e, x, s, ...) if (ERROR_SUCCESS != e) { x = HRESULT_FROM_WIN32(e); if (!FAILED(x)) { x = E_FAIL; } Dutil_RootFailure(__FILE__, __LINE__, x); ExitTraceSource(d, x, s, __VA_ARGS__); goto LExit; } #define ExitOnOptionalXmlQueryFailureSource(d, x, b, s, ...) { { if (S_FALSE == x || E_NOTFOUND == x) { b = FALSE; x = S_OK; } else { b = SUCCEEDED(x); } }; ExitOnRootFailureSource(d, x, s, __VA_ARGS__); } #define ExitOnRequiredXmlQueryFailureSource(d, x, s, ...) { if (S_FALSE == x) { x = E_NOTFOUND; } ExitOnRootFailureSource(d, x, s, __VA_ARGS__); } +#define ExitOnWaitObjectFailureSource(d, x, b, s, ...) { { if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == x) { b = TRUE; x = S_OK; } else { b = FALSE; } }; ExitOnFailureSource(d, x, s, __VA_ARGS__); } #define ExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) #define ExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) @@ -145,6 +146,7 @@ void DAPI Dutil_RootFailure(__in_z LPCSTR szFile, __in int iLine, __in HRESULT h #define ExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DEFAULT, e, x, s, __VA_ARGS__) #define ExitOnOptionalXmlQueryFailure(x, b, s, ...) ExitOnOptionalXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, b, s, __VA_ARGS__) #define ExitOnRequiredXmlQueryFailure(x, s, ...) ExitOnRequiredXmlQueryFailureSource(DUTIL_SOURCE_DEFAULT, x, s, __VA_ARGS__) +#define ExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_DEFAULT, x, b, s, __VA_ARGS__) // release macros #define ReleaseObject(x) if (x) { x->Release(); } diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h index f1dd5d1a..664c21e5 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h @@ -63,6 +63,7 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_VERUTIL, DUTIL_SOURCE_WNDUTIL, DUTIL_SOURCE_ENVUTIL, + DUTIL_SOURCE_THRDUTIL, DUTIL_SOURCE_EXTERNAL = 256, } DUTIL_SOURCE; diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h index d5ab9242..d61d91b5 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h @@ -58,7 +58,7 @@ HRESULT DAPI ProcExecute( HRESULT DAPI ProcWaitForCompletion( __in HANDLE hProcess, __in DWORD dwTimeout, - __out DWORD *pReturnCode + __out_opt DWORD* pdwReturnCode ); HRESULT DAPI ProcWaitForIds( __in_ecount(cProcessIds) const DWORD* pdwProcessIds, diff --git a/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h b/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h new file mode 100644 index 00000000..47e159a1 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/thrdutil.h @@ -0,0 +1,22 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************** + ThrdWaitForCompletion - waits for thread to complete and gets return code. + + *******************************************************************/ +HRESULT DAPI ThrdWaitForCompletion( + __in HANDLE hThread, + __in DWORD dwTimeout, + __out_opt DWORD* pdwReturnCode + ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libs/dutil/WixToolset.DUtil/monutil.cpp b/src/libs/dutil/WixToolset.DUtil/monutil.cpp index 6ad75b56..10954164 100644 --- a/src/libs/dutil/WixToolset.DUtil/monutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/monutil.cpp @@ -16,6 +16,7 @@ #define MonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_MONUTIL, p, x, s, __VA_ARGS__) #define MonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_MONUTIL, e, x, s, __VA_ARGS__) #define MonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_MONUTIL, g, x, s, __VA_ARGS__) +#define MonExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_MONUTIL, x, b, s, __VA_ARGS__) const int MON_THREAD_GROWTH = 5; const int MON_ARRAY_GROWTH = 40; @@ -1101,11 +1102,12 @@ static DWORD WINAPI WaiterThread( { HRESULT hr = S_OK; HRESULT hrTemp = S_OK; - DWORD dwRet = 0; BOOL fAgain = FALSE; BOOL fContinue = TRUE; BOOL fNotify = FALSE; BOOL fRet = FALSE; + BOOL fTimedOut = FALSE; + DWORD dwSignaledIndex = 0; MSG msg = { }; MON_ADD_MESSAGE *pAddMessage = NULL; MON_REMOVE_MESSAGE *pRemoveMessage = NULL; @@ -1128,13 +1130,14 @@ static DWORD WINAPI WaiterThread( do { - dwRet = ::WaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE); + hr = AppWaitForMultipleObjects(pWaiterContext->cHandles - pWaiterContext->cRequestsFailing, pWaiterContext->rgHandles, FALSE, pWaiterContext->cRequestsPending > 0 ? dwWait : INFINITE, &dwSignaledIndex); + MonExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for multiple objects."); uCurrentTime = ::GetTickCount(); uDeltaInMs = uCurrentTime - uLastTimeInMs; uLastTimeInMs = uCurrentTime; - if (WAIT_OBJECT_0 == dwRet) + if (!fTimedOut && 0 == dwSignaledIndex) { do { @@ -1391,10 +1394,10 @@ static DWORD WINAPI WaiterThread( } } while (fAgain); } - else if (dwRet > WAIT_OBJECT_0 && dwRet - WAIT_OBJECT_0 < pWaiterContext->cHandles) + else if (!fTimedOut) { // OK a handle fired - only notify if it's the actual target, and not just some parent waiting for the target child to exist - dwRequestIndex = dwRet - WAIT_OBJECT_0 - 1; + dwRequestIndex = dwSignaledIndex - 1; fNotify = (pWaiterContext->rgRequests[dwRequestIndex].dwPathHierarchyIndex == pWaiterContext->rgRequests[dwRequestIndex].cPathHierarchy - 1); // Initiate re-waits before we notify callback, to ensure we don't miss a single update @@ -1426,10 +1429,6 @@ static DWORD WINAPI WaiterThread( } } } - else if (WAIT_TIMEOUT != dwRet) - { - MonExitWithLastError(hr, "Failed to wait for multiple objects with return code %u", dwRet); - } // OK, now that we've checked all triggered handles (resetting silence period timers appropriately), check for any pending notifications that we can finally fire // And set dwWait appropriately so we awaken at the right time to fire the next pending notification (in case no further writes occur during that time) @@ -1726,10 +1725,10 @@ static LRESULT CALLBACK MonWndProc( DEV_BROADCAST_HANDLE *pHandle = NULL; DEV_BROADCAST_VOLUME *pVolume = NULL; DWORD dwUnitMask = 0; - DWORD er = ERROR_SUCCESS; WCHAR chDrive = L'\0'; BOOL fArrival = FALSE; BOOL fReturnTrue = FALSE; + BOOL fTimedOut = FALSE; CREATESTRUCT *pCreateStruct = NULL; MON_WAITER_CONTEXT *pWaiterContext = NULL; MON_STRUCT *pm = NULL; @@ -1821,24 +1820,23 @@ static LRESULT CALLBACK MonWndProc( } } - er = ::WaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); + hr = AppWaitForSingleObject(pm->internalWait.hWait, MON_THREAD_WAIT_REMOVE_DEVICE); + MonExitOnWaitObjectFailure(hr, fTimedOut, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + // Make sure any waiter thread processing really old messages can immediately know that we're no longer waiting for a response - if (WAIT_OBJECT_0 == er) + if (!fTimedOut) { // If the response ID matches what we sent, we actually got a valid reply! if (pm->internalWait.dwReceiveIteration != pm->internalWait.dwSendIteration) { - TraceError(HRESULT_FROM_WIN32(er), "Waiter thread received wrong ID reply"); + TraceError(E_UNEXPECTED, "Waiter thread received wrong ID reply"); } } - else if (WAIT_TIMEOUT == er) - { - TraceError(HRESULT_FROM_WIN32(er), "No response from any waiter thread for query remove message"); - } else { - MonExitWithLastError(hr, "WaitForSingleObject failed with non-timeout reason while waiting for response from waiter thread"); + TraceError(HRESULT_FROM_WIN32(WAIT_TIMEOUT), "No response from any waiter thread for query remove message"); } + ++pm->internalWait.dwSendIteration; } } diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h index c9e4f74a..b628d271 100644 --- a/src/libs/dutil/WixToolset.DUtil/precomp.h +++ b/src/libs/dutil/WixToolset.DUtil/precomp.h @@ -90,6 +90,7 @@ #include "timeutil.h" #include "wndutil.h" #include "thmutil.h" +#include "thrdutil.h" #include "uncutil.h" #include "uriutil.h" #include "userutil.h" diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp index 340a0cda..29f575ae 100644 --- a/src/libs/dutil/WixToolset.DUtil/procutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp @@ -17,6 +17,7 @@ #define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__) #define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__) #define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__) +#define ProcExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_PROCUTIL, x, b, s, __VA_ARGS__) // private functions @@ -403,30 +404,25 @@ LExit: extern "C" HRESULT DAPI ProcWaitForCompletion( __in HANDLE hProcess, __in DWORD dwTimeout, - __out DWORD *pReturnCode + __out_opt DWORD* pdwReturnCode ) { HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; + BOOL fTimedOut = FALSE; - // Wait for everything to finish - er = ::WaitForSingleObject(hProcess, dwTimeout); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) + // Wait for everything to finish. + hr = AppWaitForSingleObject(hProcess, dwTimeout); + ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for process to complete."); + + if (fTimedOut) { - ExitFunction1(hr = HRESULT_FROM_WIN32(er)); + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); } - - if (!::GetExitCodeProcess(hProcess, &er)) + else if (pdwReturnCode && !::GetExitCodeProcess(hProcess, pdwReturnCode)) { ProcExitWithLastError(hr, "Failed to get process return code."); } - *pReturnCode = er; - LExit: return hr; } @@ -442,10 +438,10 @@ extern "C" HRESULT DAPI ProcWaitForIds( ) { HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; HANDLE hProcess = NULL; - HANDLE * rghProcesses = NULL; + HANDLE* rghProcesses = NULL; DWORD cProcesses = 0; + BOOL fTimedOut = FALSE; rghProcesses = static_cast(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); @@ -459,16 +455,14 @@ extern "C" HRESULT DAPI ProcWaitForIds( } } - er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); - if (WAIT_FAILED == er) - { - ProcExitWithLastError(hr, "Failed to wait for process to complete."); - } - else if (WAIT_TIMEOUT == er) + hr = AppWaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds, NULL); + ProcExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for processes to complete."); + + if (fTimedOut) { - ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); + ProcExitWithRootFailure(hr, HRESULT_FROM_WIN32(WAIT_TIMEOUT), "Timed out while waiting for processes to complete."); } - + LExit: if (rghProcesses) { diff --git a/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp b/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp new file mode 100644 index 00000000..a8933a48 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/thrdutil.cpp @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define ThrdExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_THRDUTIL, x, e, s, __VA_ARGS__) +#define ThrdExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_THRDUTIL, x, s, __VA_ARGS__) +#define ThrdExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_THRDUTIL, p, x, e, s, __VA_ARGS__) +#define ThrdExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, p, x, s, __VA_ARGS__) +#define ThrdExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_THRDUTIL, p, x, e, s, __VA_ARGS__) +#define ThrdExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_THRDUTIL, p, x, s, __VA_ARGS__) +#define ThrdExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_THRDUTIL, e, x, s, __VA_ARGS__) +#define ThrdExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_THRDUTIL, g, x, s, __VA_ARGS__) +#define ThrdExitOnWaitObjectFailure(x, b, s, ...) ExitOnWaitObjectFailureSource(DUTIL_SOURCE_THRDUTIL, x, b, s, __VA_ARGS__) + +DAPI_(HRESULT) ThrdWaitForCompletion( + __in HANDLE hThread, + __in DWORD dwTimeout, + __out_opt DWORD *pdwReturnCode + ) +{ + HRESULT hr = S_OK; + BOOL fTimedOut = FALSE; + + // Wait for everything to finish. + hr = AppWaitForSingleObject(hThread, dwTimeout); + ThrdExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for thread to complete."); + + if (fTimedOut) + { + hr = HRESULT_FROM_WIN32(WAIT_TIMEOUT); + } + else if (pdwReturnCode && !::GetExitCodeThread(hThread, pdwReturnCode)) + { + ThrdExitWithLastError(hr, "Failed to get thread return code."); + } + +LExit: + return hr; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp b/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp new file mode 100644 index 00000000..e8c23469 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/AppUtilTests.cpp @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace DutilTests +{ + using namespace System; + using namespace Xunit; + using namespace WixBuildTools::TestSupport; + + public ref class AppUtil + { + public: + [Fact] + void WaitForMultipleObjectsTest() + { + HRESULT hr = S_OK; + HANDLE hOne = NULL; + HANDLE hTwo = NULL; + HANDLE rghHandles[2] = { }; + DWORD dwSignaledIndex = 0; + + try + { + hOne = ::CreateEventW(NULL, TRUE, FALSE, NULL); + if (!hOne) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + NativeAssert::Succeeded(FAILED(hr) ? hr : E_FAIL, "Failed to create event."); + } + + hTwo = ::CreateEventW(NULL, TRUE, TRUE, NULL); + if (!hTwo) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + NativeAssert::Succeeded(FAILED(hr) ? hr : E_FAIL, "Failed to create event."); + } + + rghHandles[0] = hOne; + rghHandles[1] = hTwo; + + hr = AppWaitForMultipleObjects(countof(rghHandles), rghHandles, FALSE, 0, &dwSignaledIndex); + NativeAssert::Succeeded(hr, "Failed to wait for multiple objects."); + Assert::Equal(1, dwSignaledIndex); + + rghHandles[0] = hTwo; + rghHandles[1] = hOne; + + hr = AppWaitForMultipleObjects(countof(rghHandles), rghHandles, FALSE, 0, &dwSignaledIndex); + NativeAssert::Succeeded(hr, "Failed to wait for multiple objects."); + Assert::Equal(0, dwSignaledIndex); + } + finally + { + ReleaseHandle(hOne); + ReleaseHandle(hTwo); + } + } + }; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj index a1f13239..210f50f5 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -44,6 +44,7 @@ + diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index cb0c8a73..f1d9c307 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -15,6 +15,9 @@ + + Source Files + Source Files diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.h b/src/libs/dutil/test/DUtilUnitTest/precomp.h index ac57cdd4..a5542774 100644 --- a/src/libs/dutil/test/DUtilUnitTest/precomp.h +++ b/src/libs/dutil/test/DUtilUnitTest/precomp.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3-55-g6feb