WIN32: use FlsAlloc() to cleanup epicsThreadOSD

Adjust macros for compiling for older MSVC/Win SDK versions

Try to cover missing fibres include in 7.0 SDK

Support Windows XP and above.
Also removed explicit define of _WIN32_WINNT in code if it has not
been passed on compile line. This is possibly a matter for
further discussion
This commit is contained in:
Freddie Akeroyd
2023-04-30 12:42:05 -07:00
committed by Michael Davidsaver
parent 07d18c55ba
commit f56412d6a5
3 changed files with 114 additions and 50 deletions

View File

@ -15,6 +15,15 @@ should also be read to understand what has changed since earlier releases.
## Changes made on the 7.0 branch since 7.0.7
### Fixed leak from a non-EPICS thread on WIN32
On Windows targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated. Prior to this release the allocation would not be `free()`d,
resulting in a memory leak.
A similar issue on POSIX targets was previously fixed.
### Change compiler for FreeBSD to clang
The default compiler for FreeBSD targets changes from GCC to clang.
@ -259,6 +268,32 @@ This release fixed the leak on POSIX targets.
See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241)
for WIN32 status.
### Fixed leak from a non-EPICS thread
On some targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated.
Prior to this release, on POSIX and WIN32 targets, this
allocation would not be `free()`d, resulting in a memory leak.
This release fixed the leak on POSIX and WIN32 targets (excluding
MSVC before vs2012, and the WINE runtime).
### Fixed leak from a non-EPICS thread
On some targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated.
Prior to this release, on POSIX and WIN32 targets, this
struct would not be `free()`d, resulting in a memory leak.
This release fixed the leak on POSIX targets.
See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241)
for WIN32 status.
### Fix `CHECK_RELEASE = WARN`
This now works again, it was broken in 2019 (7.0.3.1) by an errant commit.

View File

@ -22,25 +22,10 @@
#define VC_EXTRALEAN
#define STRICT
/*
* Defining this allows the *much* faster critical
* section mutex primitive to be used. Unfortunately,
* using certain of these functions drops support for W95\W98\WME
* unless we specify "delay loading" when we link with the
* DLL so that DLL entry points are not resolved until they
* are used. The code does have run time switches so
* that the more advanced calls are not called unless
* they are available in the windows OS, but this feature
* isn't going to be very useful unless we specify "delay
* loading" when we link with the DLL.
*
* It appears that the only entry point used here that causes
* portability problems with W95\W98\WME is TryEnterCriticalSection.
*/
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0400
#endif
#include <windows.h>
#if _WIN32_WINNT < 0x0501
# error Minimum supported is Windows XP
#endif
#define EPICS_PRIVATE_API

View File

@ -12,6 +12,11 @@
* Author: Jeff Hill
*/
/* pull in _WIN32_WINNT* definitions, if _WIN32_WINNT
* is not already defined it is set to max supported by SDK
*/
#include <sdkddkver.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
@ -32,14 +37,58 @@
#include "epicsAtomic.h"
#include "osdThreadPvt.h"
/* Windows Vista and higher supports fibre functions, but the
* prototypes only appear in the windows SDK 8 and above.
* VS2010 supplies sdk 7, but can be upgraded to later SDK
* To accomodate this we suuply prototypes on, for XP
* fall back to Tls*() which will build and run
* correctly for epicsThreads, but means that TLS allocations from
* epicsThreadImplicitCreate() will continue to leak (for non-EPICS threads).
*
* Also, WINE circa 5.0.3 provides the FLS storage functions, but doesn't
* actually run the dtor function.
*
* we check for existance of _WIN32_WINNT_WIN8 which will only be defined
* in SDK 8 and above. If Visa is detected and SDK < 8 we will supply
* the missing prototypes
*/
#if _WIN32_WINNT >= 0x0600 /* VISTA */
# ifdef _WIN32_WINNT_WIN8 /* Existance means using SDK 8 or higher */
# include <fibersapi.h>
# else
# include <winnt.h> /* for PFLS_CALLBACK_FUNCTION */
/* the fibers api exists in vista and above, but no header is provided until SDK 8 */
WINBASEAPI DWORD WINAPI FlsAlloc(PFLS_CALLBACK_FUNCTION);
WINBASEAPI PVOID WINAPI FlsGetValue(DWORD);
WINBASEAPI BOOL WINAPI FlsSetValue(DWORD , PVOID);
WINBASEAPI BOOL WINAPI FlsFree(DWORD);
# endif
#elif _WIN32_WINNT >= 0x0501 /* Windows XP */
typedef void (WINAPI *xPFLS_CALLBACK_FUNCTION) (void*);
static
DWORD xFlsAlloc(xPFLS_CALLBACK_FUNCTION dtor) {
(void)dtor;
return TlsAlloc();
}
# define FlsAlloc xFlsAlloc
# define FlsSetValue TlsSetValue
# define FlsGetValue TlsGetValue
# define USE_TLSALLOC_FALLBACK
#else
# error Minimum supported is Windows XP
#endif
LIBCOM_API void osdThreadHooksRun(epicsThreadId id);
void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName );
static void WINAPI epicsParmCleanupWIN32 ( void * praw );
typedef struct win32ThreadGlobal {
CRITICAL_SECTION mutex;
ELLLIST threadList;
DWORD tlsIndexThreadLibraryEPICS;
DWORD flsIndexThreadLibraryEPICS;
} win32ThreadGlobal;
typedef struct epicsThreadOSD {
@ -119,7 +168,6 @@ BOOL WINAPI DllMain (
* Don't allow user's explicitly calling FreeLibrary for Com.dll to yank
* the carpet out from under EPICS threads that are still using Com.dll
*/
#if _WIN32_WINNT >= 0x0501
/*
* Only in WXP
* That's a shame because this is probably much faster
@ -127,22 +175,7 @@ BOOL WINAPI DllMain (
success = GetModuleHandleEx (
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
( LPCTSTR ) DllMain, & dllHandle );
#else
{
char name[256];
DWORD nChar = GetModuleFileName (
hModule, name, sizeof ( name ) );
if ( nChar && nChar < sizeof ( name ) ) {
dllHandle = LoadLibrary ( name );
if ( ! dllHandle ) {
success = FALSE;
}
}
else {
success = FALSE;
}
}
#endif
if ( success ) {
success = TlsSetValue ( dllHandleIndex, dllHandle );
}
@ -207,8 +240,8 @@ static win32ThreadGlobal * fetchWin32ThreadGlobal ( void )
InitializeCriticalSection ( & pWin32ThreadGlobal->mutex );
ellInit ( & pWin32ThreadGlobal->threadList );
pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS = TlsAlloc();
if ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS == 0xFFFFFFFF ) {
pWin32ThreadGlobal->flsIndexThreadLibraryEPICS = FlsAlloc(&epicsParmCleanupWIN32);
if ( pWin32ThreadGlobal->flsIndexThreadLibraryEPICS == FLS_OUT_OF_INDEXES ) {
DeleteCriticalSection ( & pWin32ThreadGlobal->mutex );
free ( pWin32ThreadGlobal );
pWin32ThreadGlobal = 0;
@ -233,10 +266,14 @@ static void epicsParmCleanupDataWIN32 ( win32ThreadParam * pParm )
}
}
static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm )
static void WINAPI epicsParmCleanupWIN32 ( void * praw )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
if ( ! pGbl ) {
win32ThreadParam * pParm = praw;
win32ThreadGlobal * pGbl;
if(!praw)
return;
if ( ! (pGbl = fetchWin32ThreadGlobal ()) ) {
fprintf ( stderr, "epicsParmCleanupWIN32: unable to find ctx\n" );
return;
}
@ -464,7 +501,7 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter )
if ( pGbl ) {
setThreadName ( pParm->id, pParm->pName );
success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm );
success = FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, pParm );
if ( success ) {
osdThreadHooksRun ( ( epicsThreadId ) pParm );
/* printf ( "starting thread %d\n", pParm->id ); */
@ -482,15 +519,15 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter )
epicsExitCallAtThreadExits ();
/*
* CAUTION: !!!! the thread id might continue to be used after this thread exits !!!!
/* On Windows we could omit this and rely on the callback given to FlsAlloc() to free.
* However < vista doesn't implement FLS at all, and WINE (circa 5.0.3) doesn't
* implement fully (dtor never runs). So for EPICS threads, we explicitly
* free() here.
*/
if ( pGbl ) {
TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, (void*)0xdeadbeef );
if(pGbl && FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, NULL )) {
epicsParmCleanupWIN32 ( pParm );
}
epicsParmCleanupWIN32 ( pParm );
return retStat; /* this indirectly closes the thread handle */
}
@ -550,7 +587,7 @@ static win32ThreadParam * epicsThreadImplicitCreate ( void )
win32ThreadPriority = GetThreadPriority ( pParm->handle );
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm );
success = FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, pParm );
if ( ! success ) {
epicsParmCleanupWIN32 ( pParm );
pParm = 0;
@ -638,6 +675,8 @@ epicsThreadId epicsThreadCreateOpt (
return NULL;
}
/* after this point, new thread is responsible to free(pParmWIN32) */
return ( epicsThreadId ) pParmWIN32;
}
@ -685,7 +724,7 @@ static void* getMyWin32ThreadParam ( win32ThreadGlobal * pGbl )
}
pParm = ( win32ThreadParam * )
TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS );
FlsGetValue ( pGbl->flsIndexThreadLibraryEPICS );
if ( ! pParm ) {
pParm = epicsThreadImplicitCreate ();
}
@ -1046,6 +1085,11 @@ LIBCOM_API void epicsStdCall epicsThreadShowAll ( unsigned level )
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm;
#ifdef USE_TLSALLOC_FALLBACK
fprintf(epicsGetStdout(), "Warning: For this target, use of epicsThread* from non-EPICS threads\n"
" May leak memory. Recommend to upgrade to >= Window Vista\n");
#endif
if ( ! pGbl ) {
return;
}