diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 139072f5d..8c8705af0 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -15,6 +15,17 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.

Changes between 3.14.x and 3.15.0.x

+

New API to hook into thread creation

+ +

A hook API has been added allowing user-supplied functions to be called +whenever a thread starts. The calls are made from the thread's context, +and can be used to control additional thread properties not handled inside +EPICS base, e.g. setting the scheduling policy or CPU affinity (on SMP +systems).

+ +

The API also supports a mapping operation, calling a user-supplied function +for every thread that is currently running.

+

New scan rate units

Scan rates defined in the menuScan.dbd file may now be specified in seconds, diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile index 989784685..e22091712 100644 --- a/src/libCom/osi/Makefile +++ b/src/libCom/osi/Makefile @@ -104,6 +104,8 @@ THREAD_CPPFLAGS_NO += -DDONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING osdThread_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) Com_SRCS += osdThread.c +Com_SRCS += osdThreadExtra.c +Com_SRCS += osdThreadHooks.c Com_SRCS += osdMutex.c Com_SRCS += osdEvent.c Com_SRCS += osdTime.cpp diff --git a/src/libCom/osi/epicsThread.h b/src/libCom/osi/epicsThread.h index fa0a6bd70..70a0cfc93 100644 --- a/src/libCom/osi/epicsThread.h +++ b/src/libCom/osi/epicsThread.h @@ -3,8 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* Copyright (c) 2012 ITER Organization +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef epicsThreadh @@ -101,6 +101,11 @@ epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level); epicsShareFunc void epicsShareAPI epicsThreadShow( epicsThreadId id,unsigned int level); +/* Hooks called when a thread starts, map function called once for every thread */ +typedef void (*EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id); +epicsShareFunc void epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook); +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func); + typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void); epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id); diff --git a/src/libCom/osi/os/Linux/osdThread.h b/src/libCom/osi/os/Linux/osdThread.h new file mode 100644 index 000000000..957253ef3 --- /dev/null +++ b/src/libCom/osi/os/Linux/osdThread.h @@ -0,0 +1,45 @@ +/*************************************************************************\ +* 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. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INC_osdThread_H +#define INC_osdThread_H + +#include + +#include "shareLib.h" +#include "ellLib.h" +#include "epicsEvent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct epicsThreadOSD { + ELLNODE node; + pthread_t tid; + pid_t lwpId; + pthread_attr_t attr; + struct sched_param schedParam; + EPICSTHREADFUNC createFunc; + void *createArg; + epicsEventId suspendEvent; + int isSuspended; + int isEpicsThread; + int isFifoScheduled; + int isOnThreadList; + unsigned int osiPriority; + char name[1]; /* actually larger */ +} epicsThreadOSD; + +epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_osdThread_H */ diff --git a/src/libCom/osi/os/Linux/osdThreadExtra.c b/src/libCom/osi/os/Linux/osdThreadExtra.c new file mode 100644 index 000000000..bb75f904f --- /dev/null +++ b/src/libCom/osi/os/Linux/osdThreadExtra.c @@ -0,0 +1,69 @@ +/*************************************************************************\ +* 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. +* Copyright (c) 2012 ITER Organization +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This differs from the posix implementation of epicsThread by: + * - printing the Linux LWP ID instead of the POSIX thread ID in the show routines + * - installing a default thread start hook, that sets the Linux thread name to the + * EPICS thread name to make it visible on OS level, and discovers the LWP ID */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" + +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level) +{ + if (!pthreadInfo) { + fprintf(epicsGetStdout(), " NAME EPICS ID " + "LWP ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int priority = 0; + + if (pthreadInfo->tid) { + int policy; + int status = pthread_getschedparam(pthreadInfo->tid, &policy, + ¶m); + + if (!status) + priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %12p %8lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->lwpId, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended ? "SUSPEND" : "OK"); + } +} + +static void thread_hook(epicsThreadId pthreadInfo) +{ + /* Set the name of the thread's process. Limited to 16 characters. */ + char comm[16]; + + snprintf(comm, sizeof(comm), "%s", pthreadInfo->name); + prctl(PR_SET_NAME, comm, 0l, 0l, 0l); + pthreadInfo->lwpId = syscall(SYS_gettid); +} + +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault = thread_hook; +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain = thread_hook; diff --git a/src/libCom/osi/os/RTEMS/osdThread.c b/src/libCom/osi/os/RTEMS/osdThread.c index 7fa541bfd..e4166ef1c 100644 --- a/src/libCom/osi/os/RTEMS/osdThread.c +++ b/src/libCom/osi/os/RTEMS/osdThread.c @@ -39,6 +39,9 @@ #include "osdInterrupt.h" #include "epicsExit.h" +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id); +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id); + /* * Per-task variables */ @@ -164,6 +167,7 @@ threadWrapper (rtems_task_argument arg) { struct taskVar *v = (struct taskVar *)arg; + epicsThreadHooksRun(v->id); (*v->funptr)(v->parm); epicsExitCallAtThreadExits (); taskVarLock (); @@ -235,6 +239,7 @@ epicsThreadInit (void) taskVarMutex = epicsMutexMustCreate (); rtems_task_ident (RTEMS_SELF, 0, &tid); setThreadInfo (tid, "_main_", NULL, NULL); + epicsThreadHooksInit(tid); initialized = 1; epicsThreadCreate ("ImsgDaemon", 99, epicsThreadGetStackSize (epicsThreadStackSmall), @@ -639,20 +644,18 @@ showInternalTaskInfo (rtems_id tid) #endif } -static void -epicsThreadShowHeader (void) -{ - fprintf(epicsGetStdout()," PRIORITY\n"); - fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); - fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); -} - static void epicsThreadShowInfo (struct taskVar *v, unsigned int level) { + if (!v) { + fprintf(epicsGetStdout()," PRIORITY\n"); + fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); + fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); + } else { fprintf(epicsGetStdout(),"%9.8x", (int)v->id); showInternalTaskInfo (v->id); fprintf(epicsGetStdout()," %s\n", v->name); + } } void epicsThreadShow (epicsThreadId id, unsigned int level) @@ -660,7 +663,7 @@ void epicsThreadShow (epicsThreadId id, unsigned int level) struct taskVar *v; if (!id) { - epicsThreadShowHeader (); + epicsThreadShowInfo (NULL, level); return; } taskVarLock (); @@ -674,6 +677,23 @@ void epicsThreadShow (epicsThreadId id, unsigned int level) fprintf(epicsGetStdout(),"*** Thread %x does not exist.\n", (unsigned int)id); } +void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) +{ + struct taskVar *v; + + taskVarLock (); + /* + * Map tasks in the order of creation (backwards through list) + */ + for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) + continue; + while (v) { + func ((epicsThreadId)v->id); + v = v->back; + } + taskVarUnlock (); +} + void epicsThreadShowAll (unsigned int level) { struct taskVar *v; diff --git a/src/libCom/osi/os/WIN32/osdThread.c b/src/libCom/osi/os/WIN32/osdThread.c index 86f7b7df7..99360bafb 100644 --- a/src/libCom/osi/os/WIN32/osdThread.c +++ b/src/libCom/osi/os/WIN32/osdThread.c @@ -38,6 +38,9 @@ #include "ellLib.h" #include "epicsExit.h" +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id); +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id); + void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); static void threadCleanupWIN32 ( void ); @@ -226,6 +229,7 @@ static win32ThreadGlobal * fetchWin32ThreadGlobal ( void ) pWin32ThreadGlobal = 0; return 0; } + epicsThreadHooksInit (NULL); InterlockedExchange ( & initCompleted, 1 ); @@ -496,6 +500,7 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); if ( success ) { + epicsThreadHooksRun ( ( epicsThreadId ) pParm ); /* printf ( "starting thread %d\n", pParm->id ); */ ( *pParm->funptr ) ( pParm->parm ); /* printf ( "terminating thread %d\n", pParm->id ); */ @@ -510,7 +515,6 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) } epicsExitCallAtThreadExits (); - /* * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! */ @@ -947,9 +951,9 @@ static const char * epics_GetThreadPriorityAsString ( HANDLE thr ) } /* - * epicsThreadShowPrivate () + * epicsThreadShowInfo () */ -static void epicsThreadShowPrivate ( epicsThreadId id, unsigned level ) +static void epicsThreadShowInfo ( epicsThreadId id, unsigned level ) { win32ThreadParam * pParm = ( win32ThreadParam * ) id; @@ -974,6 +978,28 @@ static void epicsThreadShowPrivate ( epicsThreadId id, unsigned level ) fprintf (epicsGetStdout(),"\n" ); } +/* + * epicsThreadMap () + */ +epicsShareFunc void epicsShareAPI 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 () */ @@ -987,11 +1013,11 @@ epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) } EnterCriticalSection ( & pGbl->mutex ); - - epicsThreadShowPrivate ( 0, level ); - for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + + epicsThreadShowInfo ( 0, level ); + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { - epicsThreadShowPrivate ( ( epicsThreadId ) pParm, level ); + epicsThreadShowInfo ( ( epicsThreadId ) pParm, level ); } LeaveCriticalSection ( & pGbl->mutex ); @@ -1002,8 +1028,8 @@ epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) */ epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned level ) { - epicsThreadShowPrivate ( 0, level ); - epicsThreadShowPrivate ( id, level ); + epicsThreadShowInfo ( 0, level ); + epicsThreadShowInfo ( id, level ); } /* diff --git a/src/libCom/osi/os/default/osdThreadExtra.c b/src/libCom/osi/os/default/osdThreadExtra.c new file mode 100644 index 000000000..d1b5e67ef --- /dev/null +++ b/src/libCom/osi/os/default/osdThreadExtra.c @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Ralph Lange Date: 26 Jun 2012 */ + +/* Null default thread hooks for all platforms that do not do anything special */ + +#define epicsExportSharedSymbols +#include "shareLib.h" + +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; diff --git a/src/libCom/osi/os/default/osdThreadHooks.c b/src/libCom/osi/os/default/osdThreadHooks.c new file mode 100644 index 000000000..0d0aff5f9 --- /dev/null +++ b/src/libCom/osi/os/default/osdThreadHooks.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Ralph Lange Date: 28 Jun 2012 */ + +/* Secure hooks for epicsThread */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "ellLib.h" +#include "epicsMutex.h" +#include "epicsThread.h" + +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id); +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +#define checkStatusOnceReturn(status, message, method) \ +if((status)) { \ + fprintf(stderr,"%s error %s\n",(message),strerror((status))); \ + fprintf(stderr," %s\n",(method)); \ + return; } + +typedef struct epicsThreadHook { + ELLNODE node; + EPICS_THREAD_HOOK_ROUTINE func; +} epicsThreadHook; + +static ELLLIST startHooks = ELLLIST_INIT; +static epicsMutexId hookLock; + +epicsShareFunc void epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook) +{ + epicsThreadHook *pHook; + + pHook = calloc(1, sizeof(epicsThreadHook)); + if (!pHook) + checkStatusOnceReturn(errno, "calloc", "epicsThreadHookAdd"); + pHook->func = hook; + epicsMutexLock(hookLock); + ellAdd(&startHooks, &pHook->node); + epicsMutexUnlock(hookLock); +} + +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id) +{ + if (!hookLock) { + hookLock = epicsMutexMustCreate(); + if (epicsThreadHookDefault) + epicsThreadHookAdd(epicsThreadHookDefault); + } + if (id && epicsThreadHookMain) + epicsThreadHookMain(id); +} + +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id) +{ + epicsThreadHook *pHook; + + epicsMutexLock(hookLock); + pHook = (epicsThreadHook *) ellFirst(&startHooks); + while (pHook) { + pHook->func(id); + pHook = (epicsThreadHook *) ellNext(&pHook->node); + } + epicsMutexUnlock(hookLock); +} diff --git a/src/libCom/osi/os/posix/osdThread.c b/src/libCom/osi/os/posix/osdThread.c index 3c8844f70..2dbdb0d5f 100644 --- a/src/libCom/osi/os/posix/osdThread.c +++ b/src/libCom/osi/os/posix/osdThread.c @@ -3,6 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -34,6 +35,10 @@ #include "epicsAssert.h" #include "epicsExit.h" +epicsShareFunc void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level); +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id); +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id); + static int mutexLock(pthread_mutex_t *id) { int status; @@ -57,22 +62,6 @@ typedef struct commonAttr{ int schedPolicy; } commonAttr; -typedef struct epicsThreadOSD { - ELLNODE node; - pthread_t tid; - pthread_attr_t attr; - struct sched_param schedParam; - EPICSTHREADFUNC createFunc; - void *createArg; - epicsEventId suspendEvent; - int isSuspended; - int isEpicsThread; - int isFifoScheduled; - int isOnThreadList; - unsigned int osiPriority; - char name[1]; /* actually larger */ -} epicsThreadOSD; - #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING typedef struct { int min_pri, max_pri; @@ -366,6 +355,7 @@ static void once(void) checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadInit"); status = atexit(epicsExitCallAtExits); checkStatusOnce(status,"atexit"); + epicsThreadHooksInit(pthreadInfo); epicsThreadOnceCalled = 1; } @@ -375,7 +365,7 @@ static void * start_routine(void *arg) int status; int oldtype; sigset_t blockAllSig; - + sigfillset(&blockAllSig); pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL); status = pthread_setspecific(getpthreadInfo,arg); @@ -388,11 +378,11 @@ static void * start_routine(void *arg) pthreadInfo->isOnThreadList = 1; status = pthread_mutex_unlock(&listLock); checkStatusQuit(status,"pthread_mutex_unlock","start_routine"); + epicsThreadHooksRun(pthreadInfo); (*pthreadInfo->createFunc)(pthreadInfo->createArg); epicsExitCallAtThreadExits (); - free_threadInfo(pthreadInfo); return(0); } @@ -704,7 +694,7 @@ epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void) { return(pthreadInfo); } -epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId ( epicsThreadId threadId ) +epicsShareFunc pthread_t epicsThreadGetPosixThreadId ( epicsThreadId threadId ) { return threadId->tid; } @@ -747,27 +737,23 @@ epicsShareFunc void epicsShareAPI epicsThreadGetName(epicsThreadId pthreadInfo, name[size-1] = '\0'; } -static void showThreadInfo(epicsThreadOSD *pthreadInfo,unsigned int level) +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) { - if(!pthreadInfo) { - fprintf(epicsGetStdout()," NAME EPICS ID " - "PTHREAD ID OSIPRI OSSPRI STATE\n"); - } else { - struct sched_param param; - int policy; - int priority = 0; + epicsThreadOSD *pthreadInfo; + int status; - if(pthreadInfo->tid) { - int status; - status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); - if(!status) priority = param.sched_priority; - } - fprintf(epicsGetStdout(),"%16.16s %12p %12lu %3d%8d %8.8s\n", - pthreadInfo->name,(void *) - pthreadInfo,(unsigned long)pthreadInfo->tid, - pthreadInfo->osiPriority,priority, - pthreadInfo->isSuspended?"SUSPEND":"OK"); + epicsThreadInit(); + status = mutexLock(&listLock); + checkStatus(status, "pthread_mutex_lock epicsThreadMap"); + if (status) + return; + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while (pthreadInfo) { + func(pthreadInfo); + pthreadInfo = (epicsThreadOSD *)ellNext(&pthreadInfo->node); } + status = pthread_mutex_unlock(&listLock); + checkStatus(status, "pthread_mutex_unlock epicsThreadMap"); } epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) @@ -783,7 +769,7 @@ epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) return; pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); while(pthreadInfo) { - showThreadInfo(pthreadInfo,level); + epicsThreadShowInfo(pthreadInfo,level); pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); } status = pthread_mutex_unlock(&listLock); @@ -798,7 +784,7 @@ epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsi epicsThreadInit(); if(!showThread) { - showThreadInfo(0,level); + epicsThreadShowInfo(0,level); return; } status = mutexLock(&listLock); @@ -810,7 +796,7 @@ epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsi if (((epicsThreadId)pthreadInfo == showThread) || ((epicsThreadId)pthreadInfo->tid == showThread)) { found = 1; - showThreadInfo(pthreadInfo,level); + epicsThreadShowInfo(pthreadInfo,level); } pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); } @@ -820,7 +806,6 @@ epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsi if (!found) printf("Thread %#lx (%lu) not found.\n", (unsigned long)showThread, (unsigned long)showThread); } - epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void) { diff --git a/src/libCom/osi/os/posix/osdThread.h b/src/libCom/osi/os/posix/osdThread.h index 99cfe319a..cd26159d9 100644 --- a/src/libCom/osi/os/posix/osdThread.h +++ b/src/libCom/osi/os/posix/osdThread.h @@ -13,12 +13,30 @@ #include #include "shareLib.h" +#include "ellLib.h" +#include "epicsEvent.h" #ifdef __cplusplus extern "C" { #endif -epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId ( epicsThreadId id ); +typedef struct epicsThreadOSD { + ELLNODE node; + pthread_t tid; + pthread_attr_t attr; + struct sched_param schedParam; + EPICSTHREADFUNC createFunc; + void *createArg; + epicsEventId suspendEvent; + int isSuspended; + int isEpicsThread; + int isFifoScheduled; + int isOnThreadList; + unsigned int osiPriority; + char name[1]; /* actually larger */ +} epicsThreadOSD; + +epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); #ifdef __cplusplus } diff --git a/src/libCom/osi/os/posix/osdThreadExtra.c b/src/libCom/osi/os/posix/osdThreadExtra.c new file mode 100644 index 000000000..74b7c9ca2 --- /dev/null +++ b/src/libCom/osi/os/posix/osdThreadExtra.c @@ -0,0 +1,48 @@ +/*************************************************************************\ +* 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. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This is part of the posix implementation of epicsThread */ + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" + +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level) +{ + if(!pthreadInfo) { + fprintf(epicsGetStdout()," NAME EPICS ID " + "PTHREAD ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int policy; + int priority = 0; + + if(pthreadInfo->tid) { + int status; + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); + if(!status) priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %12p %12lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->tid, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended?"SUSPEND":"OK"); + } +} + diff --git a/src/libCom/osi/os/vxWorks/osdThread.c b/src/libCom/osi/os/vxWorks/osdThread.c index 09d451fab..63a2707c1 100644 --- a/src/libCom/osi/os/vxWorks/osdThread.c +++ b/src/libCom/osi/os/vxWorks/osdThread.c @@ -3,6 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -32,6 +33,9 @@ #include "vxLib.h" #include "epicsExit.h" +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id); +epicsShareFunc void epicsThreadHooksRun(epicsThreadId id); + #if CPU_FAMILY == MC680X0 #define ARCH_STACK_FACTOR 1 #elif CPU_FAMILY == SPARC @@ -42,6 +46,12 @@ static const unsigned stackSizeTable[epicsThreadStackBig+1] = {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR}; +/* Table and lock for epicsThreadMap() */ +#define ID_LIST_CHUNK 512 +static int *taskIdList = 0; +int taskIdListSize = 0; +static SEM_ID epicsThreadListMutex = 0; + /*The following forces atReboot to be loaded*/ extern int atRebootExtern; static struct pext { @@ -86,12 +96,23 @@ static int getOssPriorityValue(unsigned int osiPriority) static void epicsThreadInit(void) { static int lock = 0; + static int done = 0; - while(!vxTas(&lock)) taskDelay(1); - if(epicsThreadOnceMutex==0) { - epicsThreadOnceMutex = semMCreate( - SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); - assert(epicsThreadOnceMutex); + if (done) return; + + while(!vxTas(&lock)) + taskDelay(1); + + if (!done) { + epicsThreadHooksInit(NULL); + epicsThreadOnceMutex = semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); + assert(epicsThreadOnceMutex); + epicsThreadListMutex semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); + assert(epicsThreadListMutex); + taskIdList = calloc(ID_LIST_CHUNK, sizeof(int)); + assert(taskIdList); + taskIdListSize = ID_LIST_CHUNK; + done = 1; } lock = 0; } @@ -157,6 +178,7 @@ static void createFunction(EPICSTHREADFUNC func, void *parm) taskVarAdd(tid,(int *)(char *)&papTSD); /*Make sure that papTSD is still 0 after that call to taskVarAdd*/ papTSD = 0; + epicsThreadHooksRun((epicsThreadId)tid); (*func)(parm); epicsExitCallAtThreadExits (); free(papTSD); @@ -312,6 +334,29 @@ void epicsThreadGetName (epicsThreadId id, char *name, size_t size) name[size-1] = '\0'; } +epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) +{ + int noTasks = 0; + int i; + int result; + + result = semTake(epicsThreadListMutex, WAIT_FOREVER); + assert(result == OK); + while (noTasks == 0) { + noTasks = taskIdListGet(taskIdList, taskIdListSize); + if (noTasks == taskIdListSize) { + taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); + assert(taskIdList); + taskIdListSize += ID_LIST_CHUNK; + noTasks = 0; + } + } + for (i = 0; i < noTasks; i++) { + func ((epicsThreadId)taskIdList[i]); + } + semGive(epicsThreadListMutex); +} + void epicsThreadShowAll(unsigned int level) { taskShow(0,2); diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index f38b01dbf..659b3fcd0 100644 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -89,6 +89,11 @@ epicsThreadPrivateTest_SRCS += epicsThreadPrivateTest.cpp testHarness_SRCS += epicsThreadPrivateTest.cpp TESTS += epicsThreadPrivateTest +TESTPROD_HOST += epicsThreadHooksTest +epicsThreadHooksTest_SRCS += epicsThreadHooksTest.c +testHarness_SRCS += epicsThreadHooksTest.c +TESTS += epicsThreadHooksTest + TESTPROD_HOST += epicsExitTest epicsExitTest_SRCS += epicsExitTest.c testHarness_SRCS += epicsExitTest.c diff --git a/src/libCom/test/epicsRunLibComTests.c b/src/libCom/test/epicsRunLibComTests.c index 8fa3f837f..2749a73e5 100644 --- a/src/libCom/test/epicsRunLibComTests.c +++ b/src/libCom/test/epicsRunLibComTests.c @@ -33,6 +33,7 @@ int epicsStringTest(void); int epicsThreadOnceTest(void); int epicsThreadPriorityTest(void); int epicsThreadPrivateTest(void); +int epicsThreadHooksTest(void); int epicsTimeTest(void); int epicsTypesTest(void); int macLibTest(void); @@ -87,6 +88,8 @@ void epicsRunLibComTests(void) runTest(epicsThreadPrivateTest); + runTest(epicsThreadHooksTest); + runTest(epicsTimeTest); runTest(epicsTypesTest); diff --git a/src/libCom/test/epicsThreadHooksTest.c b/src/libCom/test/epicsThreadHooksTest.c new file mode 100644 index 000000000..d19a6c523 --- /dev/null +++ b/src/libCom/test/epicsThreadHooksTest.c @@ -0,0 +1,127 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsThreadHooksTest.c */ + +#include +#include + +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsEvent.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define THREAD_NO 6 +#define HOOKS_NO 4 + +epicsThreadPrivateId threadNo; +static int order[THREAD_NO][HOOKS_NO]; +static int cnt[THREAD_NO]; +static int mine[THREAD_NO]; +static int called[THREAD_NO]; +static epicsThreadId tid[THREAD_NO]; +epicsEventId shutdown[THREAD_NO]; + +static int newThreadIndex(epicsThreadId id) +{ + int i = 0; + while (tid[i] != 0 && i < THREAD_NO) i++; + tid[i] = id; + return i; +} + +static int findThreadIndex(epicsThreadId id) +{ + int i = 0; + while (tid[i] != id && i < THREAD_NO) i++; + return i; +} + +static void atExitHook1 (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + order[no][3] = cnt[no]++; +} + +static void atExitHook2 (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + order[no][2] = cnt[no]++; +} + +static void startHook1 (epicsThreadId id) +{ + int no = newThreadIndex(id); + order[no][0] = cnt[no]++; + epicsAtThreadExit(atExitHook1, NULL); +} + +static void startHook2 (epicsThreadId id) +{ + int no = findThreadIndex(id); + order[no][1] = cnt[no]++; + epicsAtThreadExit(atExitHook2, NULL); +} + +static void my_thread (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + mine[no] = 1; + epicsEventMustWait((epicsEventId) arg); +} + +static void mapper (epicsThreadId id) +{ + called[findThreadIndex(id)]++; +} + +MAIN(epicsThreadHooksTest) +{ + int i; + int ok; + + testPlan(THREAD_NO); + epicsThreadHookAdd(startHook1); + epicsThreadHookAdd(startHook2); + + for (i = 0; i < THREAD_NO-1; i++) { + char name[10]; + + shutdown[i] = epicsEventCreate(epicsEventEmpty); + sprintf(name, "t%d", (int) i); + epicsThreadCreate(name, epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + my_thread, shutdown[i]); + } + + epicsThreadMap(mapper); + + ok = 1; + for (i = 0; i < THREAD_NO-1; i++) { + if (mine[i] && called[i] != 1) ok = 0; + } + testOk(ok, "All tasks covered once by epicsThreadMap"); + + for (i = 0; i < THREAD_NO-1; i++) { + epicsEventSignal(shutdown[i]); + } + + epicsThreadSleep(1.0); + + for (i = 0; i < THREAD_NO; i++) { + int j; + + for (j = 0; j < HOOKS_NO; j++) { + if (mine[i] && order[i][j]!=j) ok = 0; + } + if (mine[i]) + testOk(ok, "All hooks for task %d called in correct order", (int)i); + } + return testDone(); +}