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:

committed by
Michael Davidsaver

parent
07d18c55ba
commit
f56412d6a5
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user