/*************************************************************************\ * 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) 2013 ITER Organization. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /** * \file epicsThread.h * * \brief C++ and C descriptions for a thread. * * The epicsThread API is meant as a somewhat minimal interface for * multithreaded applications. It can be implemented on a wide variety of * systems with the restriction that the system MUST support a * multithreaded environment. * A POSIX pthreads version is provided. * * The interface provides the following thread facilities, * with restrictions as noted: * - Life cycle: a thread starts life as a result of a call to * epicsThreadCreate. It terminates when the thread function returns. * It should not return until it has released all resources it uses. * If a thread is expected to terminate as a natural part of its life * cycle then the thread function must return. * - epicsThreadOnce: this provides the ability to have an * initialization function that is guaranteed to be called exactly * once. * - main: if a main routine finishes its work but wants to leave other * threads running it can call epicsThreadExitMain, which should be * the last statement in main. * - Priorities: ranges between 0 and 99 with a higher number meaning * higher priority. A number of constants are defined for iocCore * specific threads. The underlying implementation may collapse the * range 0 to 99 into a smaller range; even a single priority. User * code should never rely on the existence of multiple thread * priorities to guarantee correct behavior. * - Stack Size: epicsThreadCreate accepts a stack size parameter. Three * generic sizes are available: small, medium,and large. Portable code * should always use one of the generic sizes. Some implementation may * ignore the stack size request and use a system default instead. * Virtual memory systems providing generous stack sizes can be * expected to use the system default. * - epicsThreadId: every epicsThread has an Id which gets returned by * epicsThreadCreate and is valid as long as that thread still exists. * A value of 0 always means no thread. If a threadId is used after * the thread has terminated,the results are not defined (but will * normally lead to bad things happening). Thus code that looks after * other threads MUST be aware of threads terminating. */ #ifndef epicsThreadh #define epicsThreadh #include #include "libComAPI.h" #include "compilerDependencies.h" #ifdef __cplusplus extern "C" { #endif typedef void (*EPICSTHREADFUNC)(void *parm); /** @name Some Named Thread Priorities * @{ */ #define epicsThreadPriorityMax 99 #define epicsThreadPriorityMin 0 /* some generic values */ #define epicsThreadPriorityLow 10 #define epicsThreadPriorityMedium 50 #define epicsThreadPriorityHigh 90 /* some iocCore specific values */ #define epicsThreadPriorityCAServerLow 20 #define epicsThreadPriorityCAServerHigh 40 #define epicsThreadPriorityScanLow 60 #define epicsThreadPriorityScanHigh 70 #define epicsThreadPriorityIocsh 91 #define epicsThreadPriorityBaseMax 91 /** @} */ /** Stack sizes for each stackSizeClass are implementation and CPU dependent. */ typedef enum { epicsThreadStackSmall, epicsThreadStackMedium, epicsThreadStackBig } epicsThreadStackSizeClass; typedef enum { epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess } epicsThreadBooleanStatus; /** * Get a stack size value that can be given to epicsThreadCreate(). * \param size one of the values epicsThreadStackSmall, * epicsThreadStackMedium or epicsThreadStackBig. **/ LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize( epicsThreadStackSizeClass size); /** (epicsThreadId)0 is guaranteed to be an invalid thread id */ typedef struct epicsThreadOSD *epicsThreadId; typedef epicsThreadId epicsThreadOnceId; #define EPICS_THREAD_ONCE_INIT 0 /** Perform one-time initialization. * * Run the provided function if it has not run, and is not running in * some other thread. * * For each unique epicsThreadOnceId, epicsThreadOnce guarantees that * -# myInitFunc will only be called only once. * -# myInitFunc will have returned before any other epicsThreadOnce * call using the same epicsThreadOnceId returns. * * \code * static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; * static void myInitFunc(void *arg) { ... } * static void some Function(void) { * epicsThreadOnce(&onceId, &myInitFunc, NULL); * } * \endcode */ LIBCOM_API void epicsStdCall epicsThreadOnce( epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); /** * When real-time scheduling is active, attempt any post-init operations * that preserve real-time performance. For POSIX targets this locks the * process into RAM, preventing swap-related VM faults. **/ LIBCOM_API void epicsThreadRealtimeLock(void); /** * If the main routine is done but wants to let other threads run it can * call this routine. This should be the last call in main, except the * final return. On most systems epicsThreadExitMain never returns.This * must only be called by the main thread. * * @deprecated Deprecated for lack of use. Please report any usage. * Recommended replacement is loop + epicsThreadSleep(), * epicsEventMustWait(), or similar. **/ LIBCOM_API void epicsStdCall epicsThreadExitMain(void) EPICS_DEPRECATED; /** For use with epicsThreadCreateOpt() */ typedef struct epicsThreadOpts { /** Thread priority in OSI range (cf. epicsThreadPriority*) */ unsigned int priority; /** Thread stack size, either in bytes for this architecture or * an enum epicsThreadStackSizeClass value. */ unsigned int stackSize; /** Should thread be joinable? (default (0) is not joinable). * If joinable=1, then epicsThreadMustJoin() must be called for cleanup thread resources. */ unsigned int joinable; } epicsThreadOpts; /** Default initial values for epicsThreadOpts * Applications should always use this macro to initialize an epicsThreadOpts * structure. Additional fields may be added in the future, and the order of * the fields might also change, thus code that assumes the above definition * might break if these rules are not followed. */ #define EPICS_THREAD_OPTS_INIT { \ epicsThreadPriorityLow, epicsThreadStackMedium, 0} /** \brief Allocate and start a new OS thread. * \param name A name describing this thread. Appears in various log and error message. * \param funptr The thread main function. * \param parm Passed to thread main function. * \param opts Modifiers for the new thread, or NULL to use target specific defaults. * \return NULL on error */ LIBCOM_API epicsThreadId epicsThreadCreateOpt ( const char * name, EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts ); /** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread. */ LIBCOM_API epicsThreadId epicsStdCall epicsThreadCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ); /** Short-hand for epicsThreadCreateOpt() to create an un-joinable thread. * On error calls cantProceed() */ LIBCOM_API epicsThreadId epicsStdCall epicsThreadMustCreate ( const char * name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr,void * parm ); /* This gets undefined in osdThread.h on VxWorks < 6.9 */ #define EPICS_THREAD_CAN_JOIN /** Wait for a joinable thread to exit (return from its main function) */ LIBCOM_API void epicsThreadMustJoin(epicsThreadId id); /** Block the current thread until epicsThreadResume(). */ LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void); /** Resume a thread suspended with epicsThreadSuspendSelf() */ LIBCOM_API void epicsStdCall epicsThreadResume(epicsThreadId id); /** Return thread OSI priority */ LIBCOM_API unsigned int epicsStdCall epicsThreadGetPriority( epicsThreadId id); /** Return thread OSI priority */ LIBCOM_API unsigned int epicsStdCall epicsThreadGetPrioritySelf(void); /** Change OSI priority of target thread. */ LIBCOM_API void epicsStdCall epicsThreadSetPriority( epicsThreadId id,unsigned int priority); /** Lookup the next usage OSI priority such that priority > *pPriorityJustBelow * if this is possible with the current target configuration and privlages. */ LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadHighestPriorityLevelBelow ( unsigned int priority, unsigned *pPriorityJustBelow); /** Lookup the next usage OSI priority such that priority < *pPriorityJustBelow * if this is possible with the current target configuration and privlages. */ LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadLowestPriorityLevelAbove ( unsigned int priority, unsigned *pPriorityJustAbove); /** Test if two thread IDs actually refer to the same OS thread */ LIBCOM_API int epicsStdCall epicsThreadIsEqual( epicsThreadId id1, epicsThreadId id2); /** How and why a thread can be suspended is implementation dependent. A * thread calling epicsThreadSuspendSelf() should result in this routine * returning true for that thread, but a thread may also be suspended * for other reasons. **/ LIBCOM_API int epicsStdCall epicsThreadIsSuspended(epicsThreadId id); /** \brief Block the calling thread for at least the specified time. * \param seconds Time to wait in seconds. Values <=0 blocks for the shortest possible time. */ LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds); /** \brief Query a value approximating the OS timer/scheduler resolution. * \return A value in seconds >=0 * * \warning On targets other than vxWorks and RTEMS, the quantum value often isn't * meaningful. Use of this function is discouraged in portable code. */ LIBCOM_API double epicsStdCall epicsThreadSleepQuantum(void); /** Find an epicsThreadId associated with the current thread. * For non-EPICS threads, a new epicsThreadId may be allocated. */ LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void); /** Attempt to find the first instance of a thread by name. * \return An epicsThreadId, or NULL if no such thread is currently running. * Note that a non-NULL ID may still be invalid if this call races * with thread exit. * * \warning Safe use of this function requires external knowledge that this * thread will not return. */ LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetId(const char *name); /** Return a value approximating the number of threads which this target * can run in parallel. This value is advisory. * \return >=1 */ LIBCOM_API int epicsThreadGetCPUs(void); /** Return the name of the current thread. * * \return Never NULL. Storage lifetime tied to epicsThreadId. * * This is either a copy of the string passed to epicsThread*Create*(), * or an arbitrary unique string for non-EPICS threads. */ LIBCOM_API const char * epicsStdCall epicsThreadGetNameSelf(void); /** Copy out the thread name into the provided buffer. * * Guaranteed to be null terminated. * size is number of bytes in buffer to hold name (including terminator). * Failure results in an empty string stored in name. */ LIBCOM_API void epicsStdCall epicsThreadGetName( epicsThreadId id, char *name, size_t size); /** * Is it OK for a thread to block? This can be called by support code * that does not know if it is called in a thread that should not block. * For example the errlog system calls this to decide when messages * should be displayed on the console. **/ LIBCOM_API int epicsStdCall epicsThreadIsOkToBlock(void); /** * When a thread is started the default is that it is not allowed to * block. This method can be called to change the state. For example * iocsh calls this to specify that it is OK to block. **/ LIBCOM_API void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock); /** Print to stdout information about all running EPICS threads. * \param level 0 prints minimal output. Higher values print more details. */ LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level); /** Print info about a single EPICS thread. */ LIBCOM_API void epicsStdCall 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); /** * Register a routine to be called by every new thread before the thread * function gets run. Hook routines will often register a thread exit * routine with epicsAtThreadExit() to release thread-specific resources * they have allocated. */ LIBCOM_API int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook); /** * Remove routine from the list of hooks run at thread creation time. **/ LIBCOM_API int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook); /** * Print the current list of hook function pointers. **/ LIBCOM_API void epicsThreadHooksShow(void); /** * Call func once for every known thread. **/ LIBCOM_API void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func); /** Thread local storage */ typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; /** Allocate a new thread local variable. * This variable will initially hold NULL for each thread. */ LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void); /** Free a thread local variable */ LIBCOM_API void epicsStdCall epicsThreadPrivateDelete(epicsThreadPrivateId id); /** Update thread local variable */ LIBCOM_API void epicsStdCall epicsThreadPrivateSet(epicsThreadPrivateId,void *); /** Fetch the current value of a thread local variable */ LIBCOM_API void * epicsStdCall epicsThreadPrivateGet(epicsThreadPrivateId); #ifdef __cplusplus } #endif #ifdef __cplusplus #include "epicsEvent.h" #include "epicsMutex.h" //! Interface used with class epicsThread class LIBCOM_API epicsThreadRunable { public: virtual ~epicsThreadRunable () = 0; //! Thread main function. //! C++ exceptions which propagate from this method will be caught and a warning printed. //! No other action is taken. virtual void run () = 0; //! Optional. Called via epicsThread::show() virtual void show ( unsigned int level ) const; }; extern "C" void epicsThreadCallEntryPoint ( void * ); /** \brief An OS thread * * A wrapper around the epicsThread* C API. * * \note Threads must be start() ed. */ class LIBCOM_API epicsThread { public: /** Create a new thread with the provided information. * * cf. epicsThreadOpts * \note Threads must be start() ed. * @throws epicsThread::unableToCreateThread on error. */ epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize, unsigned int priority=epicsThreadPriorityLow ); ~epicsThread () throw (); //! Actually start the thread. void start () throw (); //! Wait for the thread epicsRunnable::run() to return. void exitWait () throw (); //! Wait for the thread epicsRunnable::run() to return. //! \param delay Wait up to this many seconds. //! \return true if run() returned. false on timeout. bool exitWait ( const double delay ) throw (); //! @throws A special exitException which will be caught and ignored. //! \note This exitException doesn't not derive from std::exception static void exit (); //! cf. epicsThreadResume() void resume () throw (); //! cf. epicsThreadGetName(); void getName ( char * name, size_t size ) const throw (); //! cf. epicsThreadGetIdSelf()() epicsThreadId getId () const throw (); //! cf. epicsThreadGetPriority() unsigned int getPriority () const throw (); //! cf. epicsThreadSetPriority() void setPriority ( unsigned int ) throw (); bool priorityIsEqual ( const epicsThread & ) const throw (); bool isSuspended () const throw (); //! \return true if call through this thread's epicsRunnable::run() bool isCurrentThread () const throw (); bool operator == ( const epicsThread & ) const throw (); //! Say something interesting about this thread to stdout. void show ( unsigned level ) const throw (); /* these operate on the current thread */ static void suspendSelf () throw (); static void sleep (double seconds) throw (); static const char * getNameSelf () throw (); static bool isOkToBlock () throw (); static void setOkToBlock ( bool isOkToBlock ) throw (); /* exceptions */ class unableToCreateThread; private: epicsThreadRunable & runable; epicsThreadId id; epicsMutex mutex; epicsEvent event; epicsEvent exitEvent; bool * pThreadDestroyed; bool begin; bool cancel; bool terminated; bool joined; bool beginWait () throw (); epicsThread ( const epicsThread & ); epicsThread & operator = ( const epicsThread & ); friend void epicsThreadCallEntryPoint ( void * ); void printLastChanceExceptionMessage ( const char * pExceptionTypeName, const char * pExceptionContext ); /* exceptions */ class exitException {}; }; class LIBCOM_API epicsThreadPrivateBase { public: class unableToCreateThreadPrivate {}; /* exception */ protected: static void throwUnableToCreateThreadPrivate (); }; template < class T > class epicsThreadPrivate : private epicsThreadPrivateBase { public: epicsThreadPrivate (); ~epicsThreadPrivate () throw (); T * get () const throw (); void set (T *) throw (); private: epicsThreadPrivateId id; }; #endif /* __cplusplus */ #include "osdThread.h" #ifdef __cplusplus template inline epicsThreadPrivate::epicsThreadPrivate () { this->id = epicsThreadPrivateCreate (); if ( this->id == 0 ) { epicsThreadPrivateBase::throwUnableToCreateThreadPrivate (); } } template inline epicsThreadPrivate::~epicsThreadPrivate () throw () { epicsThreadPrivateDelete ( this->id ); } template inline T *epicsThreadPrivate::get () const throw () { return static_cast ( epicsThreadPrivateGet (this->id) ); } template inline void epicsThreadPrivate::set (T *pIn) throw () { epicsThreadPrivateSet ( this->id, static_cast (pIn) ); } #endif /* ifdef __cplusplus */ #endif /* epicsThreadh */