Files
epics-base/modules/libcom/src/osi/os/RTEMS-kernel/osdThread.c
2021-06-04 16:11:10 -05:00

873 lines
24 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Saskatchewan
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* RTEMS osdThread.c
* Author: W. Eric Norum
*/
/*
* We want to print out some task information which is
* normally hidden from application programs.
*/
#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <assert.h>
#include <syslog.h>
#include <limits.h>
#include <pthread.h>
#include <rtems.h>
#include <rtems/error.h>
#include "epicsStdio.h"
#include "errlog.h"
#include "epicsMutex.h"
#include "epicsString.h"
#include "epicsThread.h"
#include "cantProceed.h"
#include "osiUnistd.h"
#include "osdInterrupt.h"
#include "epicsExit.h"
#include "epicsAtomic.h"
LIBCOM_API void osdThreadHooksRun(epicsThreadId id);
LIBCOM_API void osdThreadHooksRunMain(epicsThreadId id);
/*
* Per-task variables
*/
struct taskVar {
struct taskVar *forw;
struct taskVar *back;
char *name;
rtems_id id;
rtems_id join_barrier; /* only valid if joinable */
int refcnt;
int joinable;
EPICSTHREADFUNC funptr;
void *parm;
unsigned int threadVariableCapacity;
void **threadVariables;
};
static struct epicsMutexOSD *taskVarMutex;
static struct taskVar *taskVarHead;
#define RTEMS_NOTEPAD_TASKVAR 11
/*
* Support for `once-only' execution
*/
static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */
static struct epicsMutexOSD *onceMutex;
static
void epicsMutexOsdMustLock(struct epicsMutexOSD * L)
{
while(epicsMutexOsdLock(L)!=epicsMutexLockOK) {
cantProceed("epicsThreadOnce() mutex error");
}
}
/*
* Just map osi 0 to 99 into RTEMS 199 to 100
* For RTEMS lower number means higher priority
* RTEMS = 100 + (99 - osi)
* = 199 - osi
* osi = 199 - RTEMS
*/
int epicsThreadGetOsiPriorityValue(int ossPriority)
{
if (ossPriority < 100) {
return epicsThreadPriorityMax;
}
else if (ossPriority > 199) {
return epicsThreadPriorityMin;
}
else {
return (199u - (unsigned int)ossPriority);
}
}
int epicsThreadGetOssPriorityValue(unsigned int osiPriority)
{
if (osiPriority > 99) {
return 100;
}
else {
return (199 - (signed int)osiPriority);
}
}
/*
* epicsThreadLowestPriorityLevelAbove ()
*/
LIBCOM_API epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove
(unsigned int priority, unsigned *pPriorityJustAbove)
{
unsigned newPriority = priority + 1;
newPriority = priority + 1;
if (newPriority <= 99) {
*pPriorityJustAbove = newPriority;
return epicsThreadBooleanStatusSuccess;
}
return epicsThreadBooleanStatusFail;
}
/*
* epicsThreadHighestPriorityLevelBelow ()
*/
LIBCOM_API epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow
(unsigned int priority, unsigned *pPriorityJustBelow)
{
unsigned newPriority = priority - 1;
if (newPriority <= 99) {
*pPriorityJustBelow = newPriority;
return epicsThreadBooleanStatusSuccess;
}
return epicsThreadBooleanStatusFail;
}
unsigned int
epicsThreadGetStackSize (epicsThreadStackSizeClass size)
{
unsigned int stackSize = 11000;
switch(size) {
case epicsThreadStackSmall: stackSize = 5000; break;
case epicsThreadStackMedium: stackSize = 8000; break;
case epicsThreadStackBig: break;
default:
errlogPrintf("epicsThreadGetStackSize illegal argument");
break;
}
if (stackSize < RTEMS_MINIMUM_STACK_SIZE)
stackSize = RTEMS_MINIMUM_STACK_SIZE;
return stackSize;
}
/*
* Ensure integrity of task variable list
*/
static void
taskVarLock (void)
{
epicsMutexOsdMustLock (taskVarMutex);
}
static void
taskVarUnlock (void)
{
epicsMutexOsdUnlock (taskVarMutex);
}
static
void taskUnref(struct taskVar *v)
{
int ref = epicsAtomicDecrIntT(&v->refcnt);
assert(ref>=0);
if(ref>0) return;
if (v->joinable) {
rtems_barrier_delete(v->join_barrier);
}
free (v->threadVariables);
free (v->name);
free (v);
}
/*
* EPICS threads destroy themselves by returning from the thread entry function.
* This simple wrapper provides the same semantics on RTEMS.
*/
static rtems_task
threadWrapper (rtems_task_argument arg)
{
struct taskVar *v = (struct taskVar *)arg;
osdThreadHooksRun((epicsThreadId)v->id);
(*v->funptr)(v->parm);
epicsExitCallAtThreadExits ();
taskVarLock ();
if (v->back)
v->back->forw = v->forw;
else
taskVarHead = v->forw;
if (v->forw)
v->forw->back = v->back;
taskVarUnlock ();
if(v->joinable) {
rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT);
if(sc!=RTEMS_SUCCESSFUL)
cantProceed("oops %s\n", rtems_status_text(sc));
}
taskUnref(v);
rtems_task_delete (RTEMS_SELF);
}
/*
* The task wrapper takes care of cleanup
*/
void epicsThreadExitMain (void)
{
cantProceed("epicsThreadExitMain() has been deprecated for lack of usage."
" Please report if you see this message.");
}
static rtems_status_code
setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
void *parm, int joinable)
{
struct taskVar *v;
uint32_t note;
rtems_status_code sc = RTEMS_SUCCESSFUL;
v = mallocMustSucceed (sizeof *v, "epicsThreadCreate_vars");
v->name = epicsStrDup(name);
v->id = tid;
v->funptr = funptr;
v->parm = parm;
v->joinable = joinable;
v->refcnt = joinable ? 2 : 1;
v->threadVariableCapacity = 0;
v->threadVariables = NULL;
if (joinable) {
char c[3];
strncpy(c, v->name, 3);
sc = rtems_barrier_create(rtems_build_name('~', c[0], c[1], c[2]),
RTEMS_BARRIER_AUTOMATIC_RELEASE | RTEMS_LOCAL,
2, &v->join_barrier);
if (sc != RTEMS_SUCCESSFUL) {
free(v);
return sc;
}
}
note = (uint32_t)v;
rtems_task_set_note (tid, RTEMS_NOTEPAD_TASKVAR, note);
taskVarLock ();
v->forw = taskVarHead;
v->back = NULL;
if (v->forw)
v->forw->back = v;
taskVarHead = v;
taskVarUnlock ();
if (funptr) {
sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v);
}
if (sc != RTEMS_SUCCESSFUL) {
if (joinable) {
rtems_barrier_delete(v->join_barrier);
}
free(v);
}
return sc;
}
/*
* OS-dependent initialization
* No need to worry about making this thread-safe since
* it must be called before epicsThreadCreate creates
* any new threads.
*/
static void
epicsThreadInit (void)
{
if (!initialized) {
rtems_id tid;
rtems_task_priority old;
rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old);
onceMutex = epicsMutexOsdCreate();
taskVarMutex = epicsMutexOsdCreate();
if (!onceMutex || !taskVarMutex)
cantProceed("epicsThreadInit() can't create global mutexes\n");
rtems_task_ident (RTEMS_SELF, 0, &tid);
if(setThreadInfo (tid, "_main_", NULL, NULL, 0) != RTEMS_SUCCESSFUL)
cantProceed("epicsThreadInit() unable to setup _main_");
osdThreadHooksRunMain((epicsThreadId)tid);
initialized = 1;
epicsThreadCreate ("ImsgDaemon", 99,
epicsThreadGetStackSize (epicsThreadStackSmall),
InterruptContextMessageDaemon, NULL);
}
}
void epicsThreadRealtimeLock(void)
{}
/*
* Create and start a new thread
*/
epicsThreadId
epicsThreadCreateOpt (
const char * name,
EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
{
unsigned int stackSize;
rtems_id tid;
rtems_status_code sc;
char c[4];
if (!initialized)
epicsThreadInit();
if (!opts) {
static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
opts = &opts_default;
}
stackSize = opts->stackSize;
if (stackSize <= epicsThreadStackBig)
stackSize = epicsThreadGetStackSize(stackSize);
if (stackSize < RTEMS_MINIMUM_STACK_SIZE) {
errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n",
name, stackSize);
stackSize = RTEMS_MINIMUM_STACK_SIZE;
}
strncpy (c, name, sizeof c);
sc = rtems_task_create (rtems_build_name (c[0], c[1], c[2], c[3]),
epicsThreadGetOssPriorityValue (opts->priority),
stackSize,
RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0),
RTEMS_FLOATING_POINT|RTEMS_LOCAL,
&tid);
if (sc != RTEMS_SUCCESSFUL) {
errlogPrintf ("epicsThreadCreate create failure for %s: %s\n",
name, rtems_status_text(sc));
return 0;
}
sc = setThreadInfo (tid, name, funptr, parm, opts->joinable);
if (sc != RTEMS_SUCCESSFUL) {
errlogPrintf ("epicsThreadCreate create failure during setup for %s: %s\n",
name, rtems_status_text(sc));
rtems_task_delete(tid);
return 0;
}
return (epicsThreadId)tid;
}
epicsThreadId
threadMustCreate (const char *name,
unsigned int priority, unsigned int stackSize,
EPICSTHREADFUNC funptr,void *parm)
{
epicsThreadId tid = epicsThreadCreate(name, priority, stackSize, funptr, parm);
if (tid == NULL)
cantProceed(0);
return tid;
}
void epicsThreadMustJoin(epicsThreadId id)
{
rtems_id target_tid = (rtems_id)id, self_tid;
struct taskVar *v = 0;
rtems_task_ident (RTEMS_SELF, 0, &self_tid);
{
uint32_t note;
rtems_task_get_note (target_tid, RTEMS_NOTEPAD_TASKVAR, &note);
v = (void *)note;
}
/* 'v' may be NULL if 'id' represents a non-EPICS thread other than _main_. */
if(!v || !v->joinable) {
if(epicsThreadGetIdSelf()==id) {
errlogPrintf("Warning: %s thread self-join of unjoinable\n", v ? v->name : "non-EPICS thread");
} else {
/* try to error nicely, however in all likelyhood 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", v->name);
}
return;
} else if(target_tid!=self_tid) {
/* wait for target to complete */
rtems_status_code sc = rtems_barrier_wait(v->join_barrier, RTEMS_NO_TIMEOUT);
if(sc!=RTEMS_SUCCESSFUL)
cantProceed("oopsj %s\n", rtems_status_text(sc));
if(sc != RTEMS_SUCCESSFUL) {
errlogPrintf("epicsThreadMustJoin('%s') -> %s\n", v->name, rtems_status_text(sc));
}
}
v->joinable = 0;
taskUnref(v);
/* target task may be deleted.
* self task is not deleted, even for self join.
*/
}
void
epicsThreadSuspendSelf (void)
{
rtems_status_code sc;
sc = rtems_task_suspend (RTEMS_SELF);
if (sc != RTEMS_SUCCESSFUL)
errlogPrintf("epicsThreadSuspendSelf failed: %s\n",
rtems_status_text(sc));
}
void epicsThreadResume(epicsThreadId id)
{
rtems_id tid = (rtems_id)id;
rtems_status_code sc;
sc = rtems_task_resume (tid);
if (sc != RTEMS_SUCCESSFUL)
errlogPrintf("epicsThreadResume failed: %s\n",
rtems_status_text (sc));
}
unsigned int epicsThreadGetPriority(epicsThreadId id)
{
rtems_id tid = (rtems_id)id;
rtems_status_code sc;
rtems_task_priority pri;
sc = rtems_task_set_priority (tid, RTEMS_CURRENT_PRIORITY, &pri);
if (sc != RTEMS_SUCCESSFUL)
errlogPrintf("epicsThreadGetPriority failed: %s\n",
rtems_status_text(sc));
return epicsThreadGetOsiPriorityValue(pri);
}
unsigned int epicsThreadGetPrioritySelf(void)
{
return epicsThreadGetPriority((epicsThreadId)RTEMS_SELF);
}
void
epicsThreadSetPriority (epicsThreadId id,unsigned int osip)
{
rtems_id tid = (rtems_id)id;
rtems_status_code sc;
rtems_task_priority pri = epicsThreadGetOssPriorityValue(osip);
sc = rtems_task_set_priority (tid, pri, &pri);
if (sc != RTEMS_SUCCESSFUL)
errlogPrintf("epicsThreadSetPriority failed: %s\n",
rtems_status_text(sc));
}
int
epicsThreadIsEqual (epicsThreadId id1, epicsThreadId id2)
{
return (id1 == id2);
}
int
epicsThreadIsSuspended (epicsThreadId id)
{
rtems_id tid = (rtems_id)id;
rtems_status_code sc;
switch (sc = rtems_task_is_suspended (tid)) {
case RTEMS_SUCCESSFUL:
return 0;
case RTEMS_ALREADY_SUSPENDED:
return 1;
default:
return 1;
}
}
void
epicsThreadSleep (double seconds)
{
rtems_status_code sc;
rtems_interval delay;
extern double rtemsTicksPerSecond_double;
if (seconds > 0.0) {
seconds *= rtemsTicksPerSecond_double;
seconds += 0.99999999; /* 8 9s here is optimal */
delay = (seconds >= INT_MAX) ? INT_MAX : (int) seconds;
}
else { /* seconds <= 0 or NAN */
delay = 0;
}
sc = rtems_task_wake_after (delay);
if(sc != RTEMS_SUCCESSFUL)
errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text(sc));
}
epicsThreadId
epicsThreadGetIdSelf (void)
{
rtems_id tid;
rtems_task_ident (RTEMS_SELF, 0, &tid);
return (epicsThreadId)tid;
}
const char *epicsThreadGetNameSelf(void)
{
uint32_t note;
struct taskVar *v;
rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, &note);
v = (void *)note;
return v->name;
}
void epicsThreadGetName (epicsThreadId id, char *name, size_t size)
{
rtems_id tid = (rtems_id)id;
struct taskVar *v;
int haveName = 0;
taskVarLock ();
for (v=taskVarHead ; v != NULL ; v=v->forw) {
if (v->id == tid) {
strncpy(name, v->name, size);
haveName = 1;
break;
}
}
taskVarUnlock ();
if (!haveName) {
#if (__RTEMS_MAJOR__>4 || \
(__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || \
(__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99))
if (_Objects_Get_name_as_string((rtems_id)id, size, name) != NULL)
haveName = 1;
#else
/*
* Try to get the RTEMS task name
*/
Thread_Control *thr;
Objects_Locations l;
if ((thr=_Thread_Get(tid, &l)) != NULL) {
if (OBJECTS_LOCAL == l) {
int length;
Objects_Information *oi = _Objects_Get_information(tid);
if (oi->name_length >= size)
length = size - 1;
else
length = oi->name_length;
if (oi->is_string)
strncpy(name, thr->Object.name, length);
else
_Objects_Copy_name_raw( &thr->Object.name, name, length);
name[length] = '\0';
haveName = 1;
}
_Thread_Enable_dispatch();
}
#endif
}
if (!haveName)
snprintf(name, size, "0x%lx", (long)tid);
name[size-1] = '\0';
}
epicsThreadId epicsThreadGetId (const char *name)
{
struct taskVar *v;
rtems_id tid = 0;
/*
* Linear search is good enough since this routine
* is invoked only by command-line calls.
*/
taskVarLock ();
for (v = taskVarHead ; v != NULL ; v = v->forw) {
if (strcmp (name, v->name) == 0) {
tid = v->id;
break;
}
}
taskVarUnlock ();
return (epicsThreadId)tid;
}
/*
* Ensure func() is run only once.
*/
void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
{
#define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1
if (!initialized) epicsThreadInit();
epicsMutexOsdMustLock(onceMutex);
if (*id != EPICS_THREAD_ONCE_DONE) {
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
epicsMutexOsdUnlock(onceMutex);
func(arg);
epicsMutexOsdMustLock(onceMutex);
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if (*id == epicsThreadGetIdSelf()) {
epicsMutexOsdUnlock(onceMutex);
cantProceed("Recursive epicsThreadOnce() initialization\n");
} else
while (*id != EPICS_THREAD_ONCE_DONE) {
/* Another thread is in the above func(arg) call. */
epicsMutexOsdUnlock(onceMutex);
epicsThreadSleep(epicsThreadSleepQuantum());
epicsMutexOsdMustLock(onceMutex);
}
}
epicsMutexOsdUnlock(onceMutex);
}
/*
* Thread private storage implementation based on the vxWorks
* implementation by Andrew Johnson APS/ASD.
*/
epicsThreadPrivateId epicsThreadPrivateCreate ()
{
unsigned int taskVarIndex;
static volatile unsigned int threadVariableCount = 0;
if (!initialized) epicsThreadInit ();
taskVarLock ();
taskVarIndex = ++threadVariableCount;
taskVarUnlock ();
return (epicsThreadPrivateId)taskVarIndex;
}
void epicsThreadPrivateDelete (epicsThreadPrivateId id)
{
/* empty */
}
void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt)
{
unsigned int varIndex = (unsigned int)id;
uint32_t note;
struct taskVar *v;
int i;
rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, &note);
v = (struct taskVar *)note;
if (varIndex >= v->threadVariableCapacity) {
v->threadVariables = realloc (v->threadVariables,
(varIndex + 1) * sizeof(void *));
if (v->threadVariables == NULL)
cantProceed("epicsThreadPrivateSet realloc failed\n");
for (i = v->threadVariableCapacity ; i < varIndex ; i++)
v->threadVariables[i] = NULL;
v->threadVariableCapacity = varIndex + 1;
}
v->threadVariables[varIndex] = pvt;
}
void * epicsThreadPrivateGet (epicsThreadPrivateId id)
{
unsigned int varIndex = (unsigned int)id;
uint32_t note;
struct taskVar *v;
rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, &note);
v = (struct taskVar *)note;
if (varIndex >= v->threadVariableCapacity)
return NULL;
return v->threadVariables[varIndex];
}
/*
* Show task info
*/
struct bitmap {
char *msg;
unsigned long mask;
unsigned long state;
};
static void
showBitmap (char *cbuf, unsigned long bits, const struct bitmap *bp)
{
for ( ; bp->msg != NULL ; bp++) {
if ((bp->mask & bits) == bp->state) {
strcpy (cbuf, bp->msg);
cbuf += strlen (bp->msg);
}
}
}
static void
showInternalTaskInfo (rtems_id tid)
{
#ifdef __RTEMS_VIOLATE_KERNEL_VISIBILITY__
int epicsPri;
Thread_Control *the_thread;
Objects_Locations location;
static Thread_Control thread;
static char bitbuf[120];
static const struct bitmap taskState[] = {
{ "RUN", STATES_ALL_SET, STATES_READY },
{ "DORM", STATES_DORMANT, STATES_DORMANT },
{ "SUSP", STATES_SUSPENDED, STATES_SUSPENDED },
{ "TRANS", STATES_TRANSIENT, STATES_TRANSIENT },
{ "Delay", STATES_DELAYING, STATES_DELAYING },
{ "Wtime", STATES_WAITING_FOR_TIME, STATES_WAITING_FOR_TIME },
{ "Wbuf", STATES_WAITING_FOR_BUFFER, STATES_WAITING_FOR_BUFFER },
{ "Wseg", STATES_WAITING_FOR_SEGMENT, STATES_WAITING_FOR_SEGMENT },
{ "Wmsg" , STATES_WAITING_FOR_MESSAGE, STATES_WAITING_FOR_MESSAGE },
{ "Wevnt", STATES_WAITING_FOR_EVENT, STATES_WAITING_FOR_EVENT },
{ "Wsem", STATES_WAITING_FOR_SEMAPHORE,STATES_WAITING_FOR_SEMAPHORE },
{ "Wmtx", STATES_WAITING_FOR_MUTEX, STATES_WAITING_FOR_MUTEX },
{ "Wjoin", STATES_WAITING_FOR_JOIN_AT_EXIT,STATES_WAITING_FOR_JOIN_AT_EXIT },
{ "Wrpc", STATES_WAITING_FOR_RPC_REPLY,STATES_WAITING_FOR_RPC_REPLY },
{ "Wrate", STATES_WAITING_FOR_PERIOD, STATES_WAITING_FOR_PERIOD },
{ "Wsig", STATES_WAITING_FOR_SIGNAL, STATES_WAITING_FOR_SIGNAL },
{ NULL, 0, 0 },
};
the_thread = _Thread_Get (tid, &location);
if (location != OBJECTS_LOCAL) {
fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***");
return;
}
thread = *the_thread;
_Thread_Enable_dispatch();
/* This looks a bit weird, but it has to support RTEMS versions both before
* and after 4.10.2 when threads changed how their priorities are stored.
*/
int policy;
struct sched_param sp;
rtems_task_priority real_priority, current_priority;
rtems_status_code sc = pthread_getschedparam(tid, &policy, &sp);
if (sc == RTEMS_SUCCESSFUL) {
real_priority = sp.sched_priority;
sc = rtems_task_set_priority(tid, RTEMS_CURRENT_PRIORITY, &current_priority);
}
if (sc != RTEMS_SUCCESSFUL) {
fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***");
return;
}
/*
* Show both real and current priorities if they differ.
* Note that the epicsThreadGetOsiPriorityValue routine is not used here.
* If a thread has a priority outside the normal EPICS range then
* that priority should be displayed, not the value truncated to
* the EPICS range.
*/
epicsPri = 199-real_priority;
if (epicsPri < 0)
fprintf(epicsGetStdout()," <0");
else if (epicsPri > 99)
fprintf(epicsGetStdout()," >99");
else
fprintf(epicsGetStdout()," %4d", epicsPri);
if (current_priority == real_priority)
fprintf(epicsGetStdout(),"%4d ", (int)current_priority);
else
fprintf(epicsGetStdout(),"%4d/%-3d", (int)real_priority, (int)current_priority);
showBitmap (bitbuf, thread.current_state, taskState);
fprintf(epicsGetStdout(),"%8.8s", bitbuf);
if (thread.current_state & (STATES_WAITING_FOR_SEMAPHORE |
STATES_WAITING_FOR_MUTEX |
STATES_WAITING_FOR_MESSAGE))
fprintf(epicsGetStdout()," %8.8x", (int)thread.Wait.id);
else
fprintf(epicsGetStdout()," %8.8s", "");
#endif
}
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)
{
struct taskVar *v;
if (!id) {
epicsThreadShowInfo (NULL, level);
return;
}
taskVarLock ();
for (v = taskVarHead ; v != NULL ; v = v->forw) {
if ((rtems_id)id == v->id) {
epicsThreadShowInfo (v, level);
taskVarUnlock ();
return;
}
}
taskVarUnlock ();
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;
epicsThreadShowInfo (NULL, level);
taskVarLock ();
/*
* Show tasks in the order of creation (backwards through list)
*/
for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw)
continue;
while (v) {
epicsThreadShowInfo (v, level);
v = v->back;
}
taskVarUnlock ();
}
double epicsThreadSleepQuantum ( void )
{
extern double rtemsTicksPerSecond_double;
return 1.0 / rtemsTicksPerSecond_double;
}
LIBCOM_API int epicsThreadGetCPUs(void)
{
#if defined(RTEMS_SMP)
return rtems_smp_get_number_of_processors();
#else
return 1;
#endif
}