Files
epics-base/modules/libcom/src/osi/os/WIN32/osdThread.c
T
2026-01-06 09:51:04 +01:00

1243 lines
35 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* 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>
#include <stdio.h>
#define VC_EXTRALEAN
#define STRICT
#include <windows.h>
#include <process.h> /* for _endthread() etc */
#include "epicsStdio.h"
#include "libComAPI.h"
#include "epicsThread.h"
#include "cantProceed.h"
#include "epicsAssert.h"
#include "ellLib.h"
#include "epicsExit.h"
#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 accommodate this we supply 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 existence 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 /* Existence 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 flsIndexThreadLibraryEPICS;
} win32ThreadGlobal;
typedef struct epicsThreadOSD {
ELLNODE node;
int refcnt;
HANDLE handle;
EPICSTHREADFUNC funptr;
void * parm;
char * pName;
DWORD id;
unsigned epicsPriority;
char isSuspended;
int joinable;
int isRunning;
int isOkToBlock;
HANDLE timer; /* waitable timer */
} win32ThreadParam;
typedef struct epicsThreadPrivateOSD {
DWORD key;
} epicsThreadPrivateOSD;
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
# define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
#endif
#define osdOrdinaryPriorityStateCount 5u
static const int osdOrdinaryPriorityList [osdOrdinaryPriorityStateCount] =
{
THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */
THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */
THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */
THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */
THREAD_PRIORITY_HIGHEST /* 2 on >= W2K ??? on W95 */
};
# define osdRealtimePriorityStateCount 14u
static const int osdRealtimePriorityList [osdRealtimePriorityStateCount] =
{
-7, /* allowed on >= W2k, but no #define supplied */
-6, /* allowed on >= W2k, but no #define supplied */
-5, /* allowed on >= W2k, but no #define supplied */
-4, /* allowed on >= W2k, but no #define supplied */
-3, /* allowed on >= W2k, but no #define supplied */
THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */
THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */
THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */
THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */
THREAD_PRIORITY_HIGHEST, /* 2 on >= W2K ??? on W95 */
3, /* allowed on >= W2k, but no #define supplied */
4, /* allowed on >= W2k, but no #define supplied */
5, /* allowed on >= W2k, but no #define supplied */
6 /* allowed on >= W2k, but no #define supplied */
};
#if defined(EPICS_BUILD_DLL)
BOOL WINAPI DllMain (
HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved )
{
static DWORD dllHandleIndex;
HMODULE dllHandle = 0;
BOOL success = TRUE;
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
dllHandleIndex = TlsAlloc ();
if ( dllHandleIndex == TLS_OUT_OF_INDEXES ) {
success = FALSE;
}
break;
case DLL_PROCESS_DETACH:
success = TlsFree ( dllHandleIndex );
break;
case DLL_THREAD_ATTACH:
/*
* 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
*/
/*
* Only in WXP
* That's a shame because this is probably much faster
*/
success = GetModuleHandleEx (
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
( LPCTSTR ) DllMain, & dllHandle );
if ( success ) {
success = TlsSetValue ( dllHandleIndex, dllHandle );
}
break;
case DLL_THREAD_DETACH:
/*
* Thread is exiting, release Com.dll. I am assuming that windows is
* smart enough to postpone the unload until this function returns.
*/
dllHandle = TlsGetValue ( dllHandleIndex );
if ( dllHandle ) {
success = FreeLibrary ( dllHandle );
}
break;
}
return success;
}
#endif
/*
* fetchWin32ThreadGlobal ()
* Search for "Synchronization and Multiprocessor Issues" in ms doc
* to understand why this is necessary and why this works on smp systems.
*/
static win32ThreadGlobal * fetchWin32ThreadGlobal ( void )
{
static win32ThreadGlobal * pWin32ThreadGlobal = 0;
static LONG initStarted = 0;
static LONG initCompleted = 0;
LONG started;
LONG done;
done = InterlockedCompareExchange ( & initCompleted, 0, 0 );
if ( done ) {
return pWin32ThreadGlobal;
}
started = InterlockedCompareExchange ( & initStarted, 0, 1 );
if ( started ) {
unsigned tries = 0u;
while ( ! InterlockedCompareExchange ( & initCompleted, 0, 0 ) ) {
/*
* I am not fond of busy loops, but since this will
* collide very infrequently and this is the lowest
* level init then perhaps this is ok
*/
Sleep ( 1 );
if ( tries++ > 1000 ) {
return 0;
}
}
return pWin32ThreadGlobal;
}
pWin32ThreadGlobal = ( win32ThreadGlobal * )
calloc ( 1, sizeof ( * pWin32ThreadGlobal ) );
if ( ! pWin32ThreadGlobal ) {
InterlockedExchange ( & initStarted, 0 );
return 0;
}
InitializeCriticalSection ( & pWin32ThreadGlobal->mutex );
ellInit ( & pWin32ThreadGlobal->threadList );
pWin32ThreadGlobal->flsIndexThreadLibraryEPICS = FlsAlloc(&epicsParmCleanupWIN32);
if ( pWin32ThreadGlobal->flsIndexThreadLibraryEPICS == FLS_OUT_OF_INDEXES ) {
DeleteCriticalSection ( & pWin32ThreadGlobal->mutex );
free ( pWin32ThreadGlobal );
pWin32ThreadGlobal = 0;
return 0;
}
InterlockedExchange ( & initCompleted, 1 );
return pWin32ThreadGlobal;
}
static void epicsParmCleanupDataWIN32 ( win32ThreadParam * pParm )
{
if ( pParm ) {
if ( pParm->handle ) {
CloseHandle ( pParm->handle );
}
if ( pParm->timer ) {
CloseHandle ( pParm->timer );
}
free ( pParm );
}
}
static void WINAPI epicsParmCleanupWIN32 ( void * praw )
{
win32ThreadParam * pParm = praw;
win32ThreadGlobal * pGbl;
if(!praw)
return;
if ( ! (pGbl = fetchWin32ThreadGlobal ()) ) {
fprintf ( stderr, "epicsParmCleanupWIN32: unable to find ctx\n" );
return;
}
if ( pParm ) {
int cnt = epicsAtomicDecrIntT(&pParm->refcnt);
if(cnt > 0) return;
assert(cnt==0);
/* fprintf ( stderr, "thread %s is exiting\n", pParm->pName ); */
EnterCriticalSection ( & pGbl->mutex );
ellDelete ( & pGbl->threadList, & pParm->node );
LeaveCriticalSection ( & pGbl->mutex );
epicsParmCleanupDataWIN32 ( pParm );
}
}
/*
* epicsThreadExitMain ()
*/
LIBCOM_API void epicsStdCall epicsThreadExitMain ( void )
{
cantProceed("epicsThreadExitMain() must no longer be used.\n");
}
/*
* osdPriorityMagFromPriorityOSI ()
*/
static unsigned osdPriorityMagFromPriorityOSI ( unsigned osiPriority, unsigned priorityStateCount )
{
unsigned magnitude;
/* optimizer will remove this one if epicsThreadPriorityMin is zero */
/* and osiPriority is unsigned */
if ( osiPriority < epicsThreadPriorityMin ) {
osiPriority = epicsThreadPriorityMin;
}
if ( osiPriority > epicsThreadPriorityMax ) {
osiPriority = epicsThreadPriorityMax;
}
magnitude = osiPriority * priorityStateCount;
magnitude /= ( epicsThreadPriorityMax - epicsThreadPriorityMin ) + 1;
return magnitude;
}
LIBCOM_API
void epicsThreadRealtimeLock(void)
{}
/*
* epicsThreadGetOsdPriorityValue ()
*/
static int epicsThreadGetOsdPriorityValue ( unsigned osiPriority )
{
const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () );
const int * pStateList;
unsigned stateCount;
unsigned magnitude;
if ( priorityClass == REALTIME_PRIORITY_CLASS ) {
stateCount = osdRealtimePriorityStateCount;
pStateList = osdRealtimePriorityList;
}
else {
stateCount = osdOrdinaryPriorityStateCount;
pStateList = osdOrdinaryPriorityList;
}
magnitude = osdPriorityMagFromPriorityOSI ( osiPriority, stateCount );
return pStateList[magnitude];
}
/*
* osiPriorityMagFromMagnitueOSD ()
*/
static unsigned osiPriorityMagFromMagnitueOSD ( unsigned magnitude, unsigned osdPriorityStateCount )
{
unsigned osiPriority;
osiPriority = magnitude * ( epicsThreadPriorityMax - epicsThreadPriorityMin );
osiPriority /= osdPriorityStateCount - 1u;
osiPriority += epicsThreadPriorityMin;
return osiPriority;
}
/*
* epicsThreadGetOsiPriorityValue ()
*/
static unsigned epicsThreadGetOsiPriorityValue ( int osdPriority )
{
const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () );
const int * pStateList;
unsigned stateCount;
unsigned magnitude;
if ( priorityClass == REALTIME_PRIORITY_CLASS ) {
stateCount = osdRealtimePriorityStateCount;
pStateList = osdRealtimePriorityList;
}
else {
stateCount = osdOrdinaryPriorityStateCount;
pStateList = osdOrdinaryPriorityList;
}
for ( magnitude = 0u; magnitude < stateCount; magnitude++ ) {
if ( osdPriority == pStateList[magnitude] ) {
break;
}
}
if ( magnitude >= stateCount ) {
fprintf ( stderr,
"Unrecognized WIN32 thread priority level %d.\n",
osdPriority );
fprintf ( stderr,
"Mapping to EPICS thread priority level epicsThreadPriorityMin.\n" );
return epicsThreadPriorityMin;
}
return osiPriorityMagFromMagnitueOSD ( magnitude, stateCount );
}
/*
* epicsThreadLowestPriorityLevelAbove ()
*/
LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadLowestPriorityLevelAbove
( unsigned int priority, unsigned * pPriorityJustAbove )
{
const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () );
epicsThreadBooleanStatus status;
unsigned stateCount;
unsigned magnitude;
if ( priorityClass == REALTIME_PRIORITY_CLASS ) {
stateCount = osdRealtimePriorityStateCount;
}
else {
stateCount = osdOrdinaryPriorityStateCount;
}
magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount );
if ( magnitude < ( stateCount - 1 ) ) {
*pPriorityJustAbove = osiPriorityMagFromMagnitueOSD ( magnitude + 1u, stateCount );
status = epicsThreadBooleanStatusSuccess;
}
else {
status = epicsThreadBooleanStatusFail;
}
return status;
}
/*
* epicsThreadHighestPriorityLevelBelow ()
*/
LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadHighestPriorityLevelBelow
( unsigned int priority, unsigned * pPriorityJustBelow )
{
const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () );
epicsThreadBooleanStatus status;
unsigned stateCount;
unsigned magnitude;
if ( priorityClass == REALTIME_PRIORITY_CLASS ) {
stateCount = osdRealtimePriorityStateCount;
}
else {
stateCount = osdOrdinaryPriorityStateCount;
}
magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount );
if ( magnitude > 0u ) {
*pPriorityJustBelow = osiPriorityMagFromMagnitueOSD ( magnitude - 1u, stateCount );
status = epicsThreadBooleanStatusSuccess;
}
else {
status = epicsThreadBooleanStatusFail;
}
return status;
}
/*
* epicsThreadGetStackSize ()
*/
LIBCOM_API unsigned int epicsStdCall
epicsThreadGetStackSize ( epicsThreadStackSizeClass stackSizeClass )
{
#define STACK_SIZE(f) (f * 0x10000 * sizeof(void *))
static const unsigned stackSizeTable[epicsThreadStackBig+1] = {
STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4)
};
if (stackSizeClass<epicsThreadStackSmall) {
fprintf ( stderr,
"epicsThreadGetStackSize illegal argument (too small)");
return stackSizeTable[epicsThreadStackBig];
}
if (stackSizeClass>epicsThreadStackBig) {
fprintf ( stderr,
"epicsThreadGetStackSize illegal argument (too large)");
return stackSizeTable[epicsThreadStackBig];
}
return stackSizeTable[stackSizeClass];
}
/*
* epicsWin32ThreadEntry()
*/
static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm = ( win32ThreadParam * ) lpParameter;
unsigned retStat = 0u;
BOOL success;
if ( pGbl ) {
success = FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, pParm );
if ( success ) {
osdThreadHooksRun ( ( epicsThreadId ) pParm );
/* printf ( "starting thread %d\n", pParm->id ); */
( *pParm->funptr ) ( pParm->parm );
/* printf ( "terminating thread %d\n", pParm->id ); */
retStat = 1;
}
else {
fprintf ( stderr, "epicsWin32ThreadEntry: unable to set private\n" );
}
}
else {
fprintf ( stderr, "epicsWin32ThreadEntry: unable to find ctx\n" );
}
epicsExitCallAtThreadExits ();
epicsAtomicSetIntT(&pParm->isRunning, 0);
/* 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 && FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, NULL )) {
epicsParmCleanupWIN32 ( pParm );
}
return retStat; /* this indirectly closes the thread handle */
}
static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
{
win32ThreadParam *pParmWIN32;
pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 );
if ( pParmWIN32 ) {
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
strcpy ( pParmWIN32->pName, pName );
pParmWIN32->isSuspended = 0;
pParmWIN32->isRunning = 1;
epicsAtomicIncrIntT(&pParmWIN32->refcnt);
#ifdef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
pParmWIN32->timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
#endif
if (pParmWIN32->timer == NULL) {
pParmWIN32->timer = CreateWaitableTimer(NULL, 0, NULL);
}
if (pParmWIN32->timer == NULL) {
free(pParmWIN32);
return NULL;
}
}
return pParmWIN32;
}
static win32ThreadParam * epicsThreadImplicitCreate ( void )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
DWORD id = GetCurrentThreadId ();
win32ThreadParam * pParm;
char name[64];
HANDLE handle;
BOOL success;
if ( ! pGbl ) {
fprintf ( stderr, "epicsThreadImplicitCreate: unable to find ctx\n" );
return 0;
}
success = DuplicateHandle ( GetCurrentProcess (), GetCurrentThread (),
GetCurrentProcess (), & handle, 0, FALSE, DUPLICATE_SAME_ACCESS );
if ( ! success ) {
return 0;
}
{
unsigned long idForFormat = id;
sprintf ( name, "win%lx", idForFormat );
}
pParm = epicsThreadParmCreate ( name );
if ( pParm ) {
int win32ThreadPriority;
pParm->handle = handle;
pParm->id = id;
pParm->isOkToBlock = 1;
win32ThreadPriority = GetThreadPriority ( pParm->handle );
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
success = FlsSetValue ( pGbl->flsIndexThreadLibraryEPICS, pParm );
if ( ! success ) {
epicsParmCleanupWIN32 ( pParm );
pParm = 0;
}
else {
EnterCriticalSection ( & pGbl->mutex );
ellAdd ( & pGbl->threadList, & pParm->node );
LeaveCriticalSection ( & pGbl->mutex );
}
}
return pParm;
}
/*
* epicsThreadCreate ()
*/
epicsThreadId epicsThreadCreateOpt (
const char * pName,
EPICSTHREADFUNC pFunc, void * pParm,
const epicsThreadOpts *opts )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParmWIN32;
unsigned int stackSize;
int osdPriority;
DWORD wstat;
BOOL bstat;
if ( ! pGbl ) {
return NULL;
}
if (!opts) {
static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
opts = &opts_default;
}
stackSize = opts->stackSize;
if (stackSize <= epicsThreadStackBig)
stackSize = epicsThreadGetStackSize(stackSize);
pParmWIN32 = epicsThreadParmCreate ( pName );
if ( pParmWIN32 == 0 ) {
return ( epicsThreadId ) pParmWIN32;
}
pParmWIN32->funptr = pFunc;
pParmWIN32->parm = pParm;
pParmWIN32->epicsPriority = opts->priority;
if(opts->joinable) {
pParmWIN32->joinable = 1;
epicsAtomicIncrIntT(&pParmWIN32->refcnt);
}
{
unsigned threadId;
pParmWIN32->handle = (HANDLE) _beginthreadex (
0, stackSize, epicsWin32ThreadEntry,
pParmWIN32,
CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
& threadId );
if ( pParmWIN32->handle == 0 ) {
epicsParmCleanupDataWIN32 ( pParmWIN32 );
return NULL;
}
/* weird win32 interface threadId parameter inconsistency */
pParmWIN32->id = ( DWORD ) threadId ;
}
setThreadName ( pParmWIN32->id, pParmWIN32->pName );
osdPriority = epicsThreadGetOsdPriorityValue (opts->priority);
bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority );
if (!bstat) {
epicsParmCleanupDataWIN32 ( pParmWIN32 );
return NULL;
}
EnterCriticalSection ( & pGbl->mutex );
ellAdd ( & pGbl->threadList, & pParmWIN32->node );
LeaveCriticalSection ( & pGbl->mutex );
wstat = ResumeThread ( pParmWIN32->handle );
if (wstat==0xFFFFFFFF) {
EnterCriticalSection ( & pGbl->mutex );
ellDelete ( & pGbl->threadList, & pParmWIN32->node );
LeaveCriticalSection ( & pGbl->mutex );
epicsParmCleanupDataWIN32 ( pParmWIN32 );
return NULL;
}
/* after this point, new thread is responsible to free(pParmWIN32) */
return ( epicsThreadId ) pParmWIN32;
}
void epicsThreadMustJoin(epicsThreadId id)
{
win32ThreadParam * pParmWIN32 = id;
if(!id) {
/* no-op */
} else if(epicsAtomicCmpAndSwapIntT(&id->joinable, 1, 0)!=1) {
if(epicsThreadGetIdSelf()==id) {
fprintf(stderr, "Warning: %s thread self-join of unjoinable\n", pParmWIN32->pName);
} else {
/* try to error nicely, however in all likelihood de-ref of
* 'id' has already caused SIGSEGV as we are racing thread exit,
* which free's 'id'.
*/
cantProceed("Error: %s thread not joinable.\n", pParmWIN32->pName);
}
} else if(epicsThreadGetIdSelf() != id) {
DWORD status = WaitForSingleObject(pParmWIN32->handle, INFINITE);
if(status != WAIT_OBJECT_0) {
/* TODO: signal error? */
}
epicsParmCleanupWIN32(pParmWIN32);
} else {
/* join self silently does nothing */
epicsParmCleanupWIN32(pParmWIN32);
}
}
/*
* getMyWin32ThreadParam ()
*/
static void* getMyWin32ThreadParam ( win32ThreadGlobal * pGbl )
{
win32ThreadParam * pParm;
if ( ! pGbl ) {
pGbl = fetchWin32ThreadGlobal ();
assert ( pGbl );
}
pParm = ( win32ThreadParam * )
FlsGetValue ( pGbl->flsIndexThreadLibraryEPICS );
if ( ! pParm ) {
pParm = epicsThreadImplicitCreate ();
}
return pParm;
}
/*
* epicsThreadSuspendSelf ()
*/
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf ()
{
DWORD stat;
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm = getMyWin32ThreadParam ( pGbl );
if ( pParm ) {
EnterCriticalSection ( & pGbl->mutex );
pParm->isSuspended = 1;
LeaveCriticalSection ( & pGbl->mutex );
}
stat = SuspendThread ( GetCurrentThread () );
assert ( stat != 0xFFFFFFFF );
}
/*
* epicsThreadResume ()
*/
LIBCOM_API void epicsStdCall epicsThreadResume ( epicsThreadId id )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm = ( win32ThreadParam * ) id;
DWORD stat;
assert ( pGbl );
EnterCriticalSection ( & pGbl->mutex );
stat = ResumeThread ( pParm->handle );
pParm->isSuspended = 0;
LeaveCriticalSection ( & pGbl->mutex );
assert ( stat != 0xFFFFFFFF );
}
/*
* epicsThreadGetPriority ()
*/
LIBCOM_API unsigned epicsStdCall epicsThreadGetPriority (epicsThreadId id)
{
win32ThreadParam * pParm = ( win32ThreadParam * ) id;
return pParm->epicsPriority;
}
/*
* epicsThreadGetPrioritySelf ()
*/
LIBCOM_API unsigned epicsStdCall epicsThreadGetPrioritySelf ()
{
win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL );
if ( pParm ) {
return pParm->epicsPriority;
}
else {
int win32ThreadPriority =
GetThreadPriority ( GetCurrentThread () );
assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN );
return epicsThreadGetOsiPriorityValue ( win32ThreadPriority );
}
}
/*
* epicsThreadSetPriority ()
*/
LIBCOM_API void epicsStdCall epicsThreadSetPriority ( epicsThreadId id, unsigned priority )
{
win32ThreadParam * pParm = ( win32ThreadParam * ) id;
BOOL stat = SetThreadPriority ( pParm->handle, epicsThreadGetOsdPriorityValue (priority) );
assert (stat);
}
/*
* epicsThreadIsEqual ()
*/
LIBCOM_API int epicsStdCall epicsThreadIsEqual ( epicsThreadId id1, epicsThreadId id2 )
{
win32ThreadParam * pParm1 = ( win32ThreadParam * ) id1;
win32ThreadParam * pParm2 = ( win32ThreadParam * ) id2;
return ( id1 == id2 && pParm1->id == pParm2->id );
}
/*
* epicsThreadIsSuspended ()
*/
LIBCOM_API int epicsStdCall epicsThreadIsSuspended ( epicsThreadId id )
{
win32ThreadParam *pParm = ( win32ThreadParam * ) id;
DWORD exitCode;
BOOL stat;
stat = GetExitCodeThread ( pParm->handle, & exitCode );
if ( stat ) {
if ( exitCode != STILL_ACTIVE ) {
return 1;
}
else {
return pParm->isSuspended;
}
}
else {
return 1;
}
}
/**
* osdThreadGetTimer ()
* return stored waitable timer object for thread
*/
HANDLE osdThreadGetTimer()
{
win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL );
if (pParm) {
return pParm->timer;
} else {
return NULL;
}
}
/*
* epicsThreadSleep ()
*/
LIBCOM_API void epicsStdCall epicsThreadSleep ( double seconds )
{
/* waitable timers use 100 nanosecond intervals, like FILETIME */
static const unsigned ivalPerSec = 10000000u; /* number of 100ns intervals per second */
static const unsigned mSecPerSec = 1000u; /* milliseconds per second */
LARGE_INTEGER tmo;
HANDLE timer;
LONGLONG nIvals; /* number of intervals */
if ( seconds <= 0.0 ) {
tmo.QuadPart = 0u;
}
else if ( seconds >= INFINITE / mSecPerSec ) {
/* we need to apply a maximum wait time to stop an overflow. We choose (INFINITE - 1) milliseconds,
to be compatible with previous WaitForSingleObject() implementation */
nIvals = (LONGLONG)(INFINITE - 1) * (ivalPerSec / mSecPerSec);
tmo.QuadPart = -nIvals; /* negative value means a relative time offset for timer */
}
else {
nIvals = (LONGLONG)(seconds * ivalPerSec + 0.999999);
tmo.QuadPart = -nIvals;
}
if (tmo.QuadPart == 0) {
Sleep ( 0 );
}
else {
timer = osdThreadGetTimer();
if (!SetWaitableTimer(timer, &tmo, 0, NULL, NULL, 0)) {
fprintf ( stderr, "epicsThreadSleep: SetWaitableTimer failed %lu\n", GetLastError() );
return;
}
if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) {
fprintf ( stderr, "epicsThreadSleep: WaitForSingleObject failed %lu\n", GetLastError() );
}
}
}
/*
* epicsThreadSleepQuantum ()
*/
double epicsStdCall epicsThreadSleepQuantum ()
{
/*
* Its worth noting here that the sleep quantum on windows can
* mysteriously get better. I eventually tracked this down to
* codes that call timeBeginPeriod(1). Calling timeBeginPeriod()
* specifying a better timer resolution also increases the interrupt
* load. This appears to be related to java applet activity.
* The function timeGetDevCaps can tell us the range of periods
* that can be specified to timeBeginPeriod, but alas there
* appears to be no way to find out what the value of the global
* minimum of all timeBeginPeriod calls for all processes is.
*/
static const double secPerTick = 100e-9;
DWORD adjustment;
DWORD delay;
BOOL disabled;
BOOL success;
success = GetSystemTimeAdjustment (
& adjustment, & delay, & disabled );
if ( success ) {
return delay * secPerTick;
}
else {
return 0.0;
}
}
/*
* epicsThreadGetIdSelf ()
*/
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf (void)
{
win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL );
assert ( pParm ); /* Don't return a NULL thread id */
return ( epicsThreadId ) pParm;
}
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetId ( const char * pName )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm;
if ( ! pGbl ) {
return 0;
}
EnterCriticalSection ( & pGbl->mutex );
for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList );
pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) {
if ( pParm->pName ) {
if ( strcmp ( pParm->pName, pName ) == 0 ) {
break;
}
}
}
LeaveCriticalSection ( & pGbl->mutex );
/* !!!! warning - the thread parm could vanish at any time !!!! */
return ( epicsThreadId ) pParm;
}
/*
* epicsThreadGetNameSelf ()
*/
LIBCOM_API const char * epicsStdCall epicsThreadGetNameSelf (void)
{
win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL );
if ( pParm ) {
if ( pParm->pName ) {
return pParm->pName;
}
}
return "anonymous";
}
/*
* epicsThreadGetName ()
*/
LIBCOM_API void epicsStdCall epicsThreadGetName (
epicsThreadId id, char * pName, size_t size )
{
win32ThreadParam * pParm = ( win32ThreadParam * ) id;
if ( size ) {
size_t sizeMinusOne = size-1;
strncpy ( pName, pParm->pName, sizeMinusOne );
pName [sizeMinusOne] = '\0';
}
}
/*
* epics_GetThreadPriorityAsString ()
*/
static const char * epics_GetThreadPriorityAsString ( HANDLE thr )
{
const char * pPriName = "?????";
switch ( GetThreadPriority ( thr ) ) {
case THREAD_PRIORITY_TIME_CRITICAL:
pPriName = "tm-crit";
break;
case THREAD_PRIORITY_HIGHEST:
pPriName = "high";
break;
case THREAD_PRIORITY_ABOVE_NORMAL:
pPriName = "normal+";
break;
case THREAD_PRIORITY_NORMAL:
pPriName = "normal";
break;
case THREAD_PRIORITY_BELOW_NORMAL:
pPriName = "normal-";
break;
case THREAD_PRIORITY_LOWEST:
pPriName = "low";
break;
case THREAD_PRIORITY_IDLE:
pPriName = "idle";
break;
}
return pPriName;
}
/*
* epicsThreadShowInfo ()
*/
static void epicsThreadShowInfo ( epicsThreadId id, unsigned level )
{
win32ThreadParam * pParm = ( win32ThreadParam * ) id;
if ( pParm ) {
unsigned long idForFormat = pParm->id;
fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName,
pParm, idForFormat, pParm->epicsPriority,
epics_GetThreadPriorityAsString ( pParm->handle ),
epicsThreadIsSuspended ( id ) ? "suspend" : "ok" );
if ( level ) {
fprintf (epicsGetStdout(), " %-8p %-8p ",
pParm->handle, pParm->parm );
}
if(!epicsAtomicGetIntT(&pParm->isRunning))
fprintf (epicsGetStdout(), " ZOMBIE");
}
else {
fprintf (epicsGetStdout(),
"NAME EPICS-ID WIN32-ID EPICS-PRI WIN32-PRI STATE " );
if ( level ) {
fprintf (epicsGetStdout(), " HANDLE FUNCTION PARAMETER" );
}
}
fprintf (epicsGetStdout(),"\n" );
}
/*
* epicsThreadMap ()
*/
LIBCOM_API void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func )
{
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
win32ThreadParam * pParm;
if ( ! pGbl ) {
return;
}
EnterCriticalSection ( & pGbl->mutex );
for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList );
pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) {
func ( ( epicsThreadId ) pParm );
}
LeaveCriticalSection ( & pGbl->mutex );
}
/*
* epicsThreadShowAll ()
*/
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;
}
EnterCriticalSection ( & pGbl->mutex );
epicsThreadShowInfo ( 0, level );
for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList );
pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) {
epicsThreadShowInfo ( ( epicsThreadId ) pParm, level );
}
LeaveCriticalSection ( & pGbl->mutex );
}
/*
* epicsThreadShow ()
*/
LIBCOM_API void epicsStdCall epicsThreadShow ( epicsThreadId id, unsigned level )
{
epicsThreadShowInfo ( 0, level );
epicsThreadShowInfo ( id, level );
}
/*
* epicsThreadOnce ()
*/
LIBCOM_API void epicsStdCall epicsThreadOnce (
epicsThreadOnceId *id, void (*func)(void *), void *arg )
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE & threadOnceComplete
win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
assert ( pGbl );
EnterCriticalSection ( & pGbl->mutex );
if ( *id != EPICS_THREAD_ONCE_DONE ) {
if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
LeaveCriticalSection ( & pGbl->mutex );
func ( arg );
EnterCriticalSection ( & pGbl->mutex );
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if ( *id == epicsThreadGetIdSelf() ) {
LeaveCriticalSection ( & pGbl->mutex );
cantProceed( "Recursive epicsThreadOnce() initialization\n" );
} else
while ( *id != EPICS_THREAD_ONCE_DONE ) {
/* Another thread is in the above func(arg) call. */
LeaveCriticalSection ( & pGbl->mutex );
epicsThreadSleep ( epicsThreadSleepQuantum() );
EnterCriticalSection ( & pGbl->mutex );
}
}
LeaveCriticalSection ( & pGbl->mutex );
}
/*
* epicsThreadPrivateCreate ()
*/
LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate ()
{
epicsThreadPrivateOSD *p = ( epicsThreadPrivateOSD * ) malloc ( sizeof ( *p ) );
if ( p ) {
p->key = TlsAlloc ();
if ( p->key == 0xFFFFFFFF ) {
free ( p );
p = 0;
}
}
return p;
}
/*
* epicsThreadPrivateDelete ()
*/
LIBCOM_API void epicsStdCall epicsThreadPrivateDelete ( epicsThreadPrivateId p )
{
BOOL stat = TlsFree ( p->key );
assert ( stat );
free ( p );
}
/*
* epicsThreadPrivateSet ()
*/
LIBCOM_API void epicsStdCall epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal )
{
BOOL stat = TlsSetValue ( pPvt->key, pVal );
assert (stat);
}
/*
* epicsThreadPrivateGet ()
*/
LIBCOM_API void * epicsStdCall epicsThreadPrivateGet ( epicsThreadPrivateId pPvt )
{
return TlsGetValue ( pPvt->key );
}
/*
* epicsThreadGetCPUs ()
*/
LIBCOM_API int epicsThreadGetCPUs ( void )
{
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
if (sysinfo.dwNumberOfProcessors > 0)
return sysinfo.dwNumberOfProcessors;
return 1;
}
#ifdef TEST_CODES
void testPriorityMapping ()
{
unsigned i;
for (i=epicsThreadPriorityMin; i<epicsThreadPriorityMax; i++) {
printf ("%u %d\n", i, epicsThreadGetOsdPriorityValue (i) );
}
for (i=0; i<osdPriorityStateCount; i++) {
printf ("%d %u\n", osdPriorityList[i], epicsThreadGetOsiPriorityValue(osdPriorityList[i]));
}
return 0;
}
#endif
int epicsStdCall epicsThreadIsOkToBlock(void)
{
struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
return(pthreadInfo->isOkToBlock);
}
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
{
struct epicsThreadOSD *pthreadInfo = epicsThreadGetIdSelf();
pthreadInfo->isOkToBlock = !!isOkToBlock;
}