diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 1f5ee6f96..10cd9118a 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -574,10 +574,10 @@ broken. This fixes [lauchpad bug #1896295](https://bugs.launchpad.net/bugs/1896295). -### Support for Apple M1 (arm64) Processors +### Support for Apple M1/M2 (arm64) Processors Thanks to Jeong Han Lee this release comes with build support for Apple's new -M1 CPUs running macOS, using the target name `darwin-aarch64`. +M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. It should also be possible to build universal binaries containing code for both the Intel and arm64 processors under either target name: In the @@ -2191,6 +2191,17 @@ header and removed the need for dbScan.c to reach into the internals of its ## Changes from the 3.15 branch since 3.15.9 +### Support for Apple M1/M2 (arm64) Processors + +Thanks to Jeong Han Lee this release comes with build support for Apple's new +M1/M2 CPUs running macOS, using the target name `darwin-aarch64`. + +### Set thread names on Windows + +On MS Windows, epicsThread names are made available to the OS and debugger +using `SetThreadDescription()` if available as well as using the older +exception mechanism. + ### Fix timers on MS Windows for non-EPICS threads The waitable timer changes in 3.15.9 broke calls to `epicsThreadSleep()` and diff --git a/modules/libcom/src/osi/os/WIN32/setThreadName.cpp b/modules/libcom/src/osi/os/WIN32/setThreadName.cpp index a7a9c3e51..b218479e3 100644 --- a/modules/libcom/src/osi/os/WIN32/setThreadName.cpp +++ b/modules/libcom/src/osi/os/WIN32/setThreadName.cpp @@ -8,44 +8,86 @@ * EPICS Base is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ - #define VC_EXTRALEAN #define STRICT -#if _WIN64 -# define _WIN32_WINNT 0x400 /* defining this drops support for W95 */ -#endif #include +#include + /* - * this was copied directly from an example in visual c++ 7 documentation, - * It uses visual C++ specific keywords for exception handling, but is - * probably only useful when using the visual c++ or later debugger. - * * Usage: setThreadName (-1, "MainThread"); */ + + +static void setThreadNameVS ( DWORD dwThreadID, LPCSTR szThreadName ); +typedef HRESULT (WINAPI* setDesc_t)(HANDLE, PCWSTR); + extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ) +{ + static HMODULE hKernel = LoadLibrary("KernelBase.dll"); + static setDesc_t pSetDesc = (hKernel != NULL ? + (setDesc_t)GetProcAddress(hKernel, "SetThreadDescription") : NULL); + if (szThreadName == NULL || *szThreadName == '\0') + { + return; + } + if (pSetDesc != NULL) + { +#ifdef THREAD_SET_LIMITED_INFORMATION + DWORD thread_access = THREAD_SET_LIMITED_INFORMATION; +#else + DWORD thread_access = THREAD_SET_INFORMATION; +#endif /* ifdef THREAD_SET_LIMITED_INFORMATION */ + HANDLE hThread = OpenThread(thread_access, FALSE, dwThreadID); + if (hThread != NULL) + { + const std::string s(szThreadName); + const std::wstring ws(s.begin(), s.end()); + HRESULT hr = (*pSetDesc)(hThread, ws.c_str()); + CloseHandle(hThread); + } + } + // if SetThreadDescription() was available and we have a recent + // visual studio debugger (2017 version 15.6 or higher) attached + // then the names will already be defined. However we don't know + // this for sure, so also trigger the old exception mechanism. + // See https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code + setThreadNameVS(dwThreadID, szThreadName); +} + +static void setThreadNameVS( DWORD dwThreadID, LPCSTR szThreadName ) { #if _MSC_VER >= 1300 && defined ( _DEBUG ) +// This was copied directly from an MSDN example +// It sets the thread name by throwing a special exception that is caught by Visual Sudio +// It requires the debugger to be already attached to the process +// when the exception is thrown for the name to be registered + static const DWORD MS_VC_EXCEPTION = 0x406D1388; +#pragma pack(push,8) typedef struct tagTHREADNAME_INFO { - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; +#pragma pack(pop) THREADNAME_INFO info; info.dwType = 0x1000; info.szName = szThreadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; - +#pragma warning(push) +#pragma warning(disable: 6320 6322) __try { - RaiseException( 0x406D1388, 0, - sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info ); + RaiseException(MS_VC_EXCEPTION, 0, + sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR*)&info); } - __except(EXCEPTION_CONTINUE_EXECUTION) + __except (EXCEPTION_EXECUTE_HANDLER) { } +#pragma warning(pop) #endif }