This is Unofficial EPICS BASE Doxygen Site
osdThread.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * Copyright (c) 2012 ITER Organization
7 * EPICS BASE is distributed subject to a Software License Agreement found
8 * in file LICENSE that is included with this distribution.
9 \*************************************************************************/
10 /* osi/os/vxWorks/epicsThread.c */
11 
12 /* Author: Marty Kraimer Date: 25AUG99 */
13 
14 /* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */
15 #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h>
16 
17 #include <stddef.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <limits.h>
23 
24 #include <vxWorks.h>
25 #include <taskLib.h>
26 #include <taskVarLib.h>
27 #include <sysLib.h>
28 
29 #include "errlog.h"
30 #include "ellLib.h"
31 #include "epicsThread.h"
32 #include "cantProceed.h"
33 #include "epicsAssert.h"
34 #include "vxLib.h"
35 #include "epicsExit.h"
36 
37 #ifdef EPICS_THREAD_CAN_JOIN
38  /* The implementation of epicsThreadMustJoin() here uses 2 features
39  * of VxWorks that were first introduced in VxWorks 6.9: taskWait(),
40  * and the taskSpareFieldGet/Set routines in taskUtilLib.
41  */
42  #include <taskUtilLib.h>
43 
44  #define JOIN_WARNING_TIMEOUT (60 * sysClkRateGet())
45 
46  static SPARE_NUM joinField;
47  #define ALLOT_JOIN(tid) taskSpareNumAllot(tid, &joinField)
48 #else
49  #define ALLOT_JOIN(tid)
50 #endif
51 
52 
53 LIBCOM_API void osdThreadHooksRun(epicsThreadId id);
54 
55 #if CPU_FAMILY == MC680X0
56 #define ARCH_STACK_FACTOR 1
57 #elif CPU_FAMILY == SPARC
58 #define ARCH_STACK_FACTOR 2
59 #else
60 #define ARCH_STACK_FACTOR 2
61 #endif
62 static const unsigned stackSizeTable[epicsThreadStackBig+1] =
63  {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR};
64 
65 /* Table and lock for epicsThreadMap() */
66 #define ID_LIST_CHUNK 512
67 static int *taskIdList = 0;
69 static SEM_ID epicsThreadListMutex = 0;
70 
71 /* This routine is found in atReboot.cpp */
72 extern void atRebootRegister(void);
73 
74 /* definitions for implementation of epicsThreadPrivate */
75 static void **papTSD = 0;
76 static int nepicsThreadPrivate = 0;
77 
78 static SEM_ID epicsThreadOnceMutex = 0;
79 
80 /* Just map osi 0 to 99 into vx 100 to 199 */
81 /* remember that for vxWorks lower number means higher priority */
82 /* vx = 100 + (99 -osi) = 199 - osi*/
83 /* osi = 199 - vx */
84 
85 static unsigned int getOsiPriorityValue(int ossPriority)
86 {
87  if ( ossPriority < 100 ) {
89  }
90  else if ( ossPriority > 199 ) {
92  }
93  else {
94  return ( 199u - (unsigned int) ossPriority );
95  }
96 }
97 
98 static int getOssPriorityValue(unsigned int osiPriority)
99 {
100  if ( osiPriority > 99 ) {
101  return 100;
102  }
103  else {
104  return ( 199 - (signed int) osiPriority );
105  }
106 }
107 
108 #define THREAD_SEM_FLAGS (SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY)
109 static void epicsThreadInit(void)
110 {
111  static int lock = 0;
112  static int done = 0;
113 
114  if (done) return;
115 
116  while(!vxTas(&lock))
117  taskDelay(1);
118 
119  if (!done) {
120  epicsThreadOnceMutex = semMCreate(THREAD_SEM_FLAGS);
121  assert(epicsThreadOnceMutex);
122  epicsThreadListMutex = semMCreate(THREAD_SEM_FLAGS);
123  assert(epicsThreadListMutex);
124  taskIdList = calloc(ID_LIST_CHUNK, sizeof(int));
125  assert(taskIdList);
128  ALLOT_JOIN(0);
129  done = 1;
130  }
131  lock = 0;
132 }
133 
135 {}
136 
138 {
139 
140  if (stackSizeClass<epicsThreadStackSmall) {
141  errlogPrintf("epicsThreadGetStackSize illegal argument (too small)");
142  return stackSizeTable[epicsThreadStackBig];
143  }
144 
145  if (stackSizeClass>epicsThreadStackBig) {
146  errlogPrintf("epicsThreadGetStackSize illegal argument (too large)");
147  return stackSizeTable[epicsThreadStackBig];
148  }
149 
150  return stackSizeTable[stackSizeClass];
151 }
152 
153 struct epicsThreadOSD {};
154  /* Strictly speaking this should be a WIND_TCB, but we only need it to
155  * be able to create an epicsThreadId that is guaranteed never to be
156  * the same as any current TID, and since TIDs are pointers this works.
157  */
158 
159 void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
160 {
161  static struct epicsThreadOSD threadOnceComplete;
162  #define EPICS_THREAD_ONCE_DONE &threadOnceComplete
163  int result;
164 
165  epicsThreadInit();
166  result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
167  assert(result == OK);
168  if (*id != EPICS_THREAD_ONCE_DONE) {
169  if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
170  *id = epicsThreadGetIdSelf(); /* mark active */
171  semGive(epicsThreadOnceMutex);
172  func(arg);
173  result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
174  assert(result == OK);
175  *id = EPICS_THREAD_ONCE_DONE; /* mark done */
176  } else if (*id == epicsThreadGetIdSelf()) {
177  semGive(epicsThreadOnceMutex);
178  cantProceed("Recursive epicsThreadOnce() initialization\n");
179  } else
180  while (*id != EPICS_THREAD_ONCE_DONE) {
181  /* Another thread is in the above func(arg) call. */
182  semGive(epicsThreadOnceMutex);
184  result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
185  assert(result == OK);
186  }
187  }
188  semGive(epicsThreadOnceMutex);
189 }
190 
191 #ifdef EPICS_THREAD_CAN_JOIN
192 
193 /* The next 2 routines are not static so they appear in the back-trace
194  * of the epicsThreads that have called them.
195  */
196 void epicsThreadAwaitingJoin(int tid)
197 {
198  SEM_ID joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField);
199  STATUS status;
200 
201  if (!joinSem || (int) joinSem == ERROR)
202  return;
203 
204  /* Wait for our supervisor */
205  status = semTake(joinSem, JOIN_WARNING_TIMEOUT);
206  if (status && errno == S_objLib_OBJ_TIMEOUT) {
207  errlogPrintf("Warning: epicsThread '%s' still awaiting join\n",
209  status = semTake(joinSem, WAIT_FOREVER);
210  }
211  if (status)
212  perror("epicsThreadAwaitingJoin");
213 
214  taskSpareFieldSet(tid, joinField, 0);
215  semDelete(joinSem);
216 }
217  #define PREPARE_JOIN(tid, joinable) \
218  taskSpareFieldSet(tid, joinField, \
219  joinable ? (int) semBCreate(SEM_Q_FIFO, SEM_EMPTY) : 0)
220  #define AWAIT_JOIN(tid) \
221  epicsThreadAwaitingJoin(tid)
222 
223 #else
224 
225  #define PREPARE_JOIN(tid, joinable)
226  #define AWAIT_JOIN(tid)
227 
228 #endif
229 
231 {
232  int tid = taskIdSelf();
233 
234  taskVarAdd(tid,(int *)(char *)&papTSD);
235  papTSD = NULL; /* Initialize for this thread */
236 
238 
239  (*func)(parm);
240 
242  free(papTSD);
243  taskVarDelete(tid,(int *)(char *)&papTSD);
244 
245  AWAIT_JOIN(tid);
246 }
247 
248 #ifdef ALTIVEC
249  #define TASK_FLAGS (VX_FP_TASK | VX_ALTIVEC_TASK)
250 #else
251  #define TASK_FLAGS (VX_FP_TASK)
252 #endif
254  EPICSTHREADFUNC funptr, void * parm, const epicsThreadOpts *opts )
255 {
256  unsigned int stackSize;
257  int tid;
258 
259  epicsThreadInit();
260 
261  if (!opts) {
262  static const epicsThreadOpts opts_default = EPICS_THREAD_OPTS_INIT;
263  opts = &opts_default;
264  }
265  stackSize = opts->stackSize;
266  if (stackSize <= epicsThreadStackBig)
267  stackSize = epicsThreadGetStackSize(stackSize);
268 
269  if (stackSize < 100) {
270  errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",
271  name, stackSize);
272  return 0;
273  }
274 
275  tid = taskCreate((char *)name,getOssPriorityValue(opts->priority),
276  TASK_FLAGS, stackSize,
277  (FUNCPTR)epicsThreadEntry, (int)funptr, (int)parm,
278  0,0,0,0,0,0,0,0);
279  if (tid == ERROR) {
280  errlogPrintf("epicsThreadCreate %s failure %s\n",
281  name, strerror(errno));
282  return 0;
283  }
284 
285  PREPARE_JOIN(tid, opts->joinable);
286  taskActivate(tid);
287 
288  return (epicsThreadId)tid;
289 }
290 
292 {
293 #ifdef EPICS_THREAD_CAN_JOIN
294  const char *fn = "epicsThreadMustJoin";
295  int tid = (int) id;
296  SEM_ID joinSem;
297  STATUS status;
298 
299  if (!tid)
300  return;
301 
302  joinSem = (SEM_ID) taskSpareFieldGet(tid, joinField);
303  if ((int) joinSem == ERROR) {
304  errlogPrintf("%s: Thread %#x no longer exists.\n", fn, tid);
305  return;
306  }
307 
308  if (tid == taskIdSelf()) {
309  if (joinSem) {
310  taskSpareFieldSet(tid, joinField, 0);
311  semDelete(joinSem);
312  }
313  else {
314  errlogPrintf("%s: Thread '%s' (%#x) can't self-join.\n",
315  fn, epicsThreadGetNameSelf(), tid);
316  }
317  return;
318  }
319 
320  if (!joinSem) {
321  cantProceed("%s: Thread '%s' (%#x) is not joinable.\n",
322  fn, taskName(tid), tid);
323  return;
324  }
325 
326  semGive(joinSem); /* Rendezvous with thread */
327 
328  status = taskWait(tid, WAIT_FOREVER);
329  if (status && errno != S_objLib_OBJ_ID_ERROR) {
330  perror(fn);
331  cantProceed("%s: ", fn);
332  }
333 #endif
334 }
335 
337 {
338  STATUS status;
339 
340  status = taskSuspend(taskIdSelf());
341  if(status) errlogPrintf("epicsThreadSuspendSelf failed\n");
342 }
343 
345 {
346  int tid = (int)id;
347  STATUS status;
348 
349  status = taskResume(tid);
350  if(status) errlogPrintf("epicsThreadResume failed\n");
351 }
352 
354 {
355  errlogPrintf("epicsThreadExitMain was called for vxWorks. Why?\n");
356 }
357 
359 {
360  int tid = (int)id;
361  STATUS status;
362  int priority = 0;
363 
364  status = taskPriorityGet(tid,&priority);
365  if(status) errlogPrintf("epicsThreadGetPriority failed\n");
366  return(getOsiPriorityValue(priority));
367 }
368 
369 unsigned int epicsThreadGetPrioritySelf(void)
370 {
372 }
373 
374 void epicsThreadSetPriority(epicsThreadId id,unsigned int osip)
375 {
376  int tid = (int)id;
377  STATUS status;
378  int priority = 0;
379 
380  priority = getOssPriorityValue(osip);
381  status = taskPrioritySet(tid,priority);
382  if(status) errlogPrintf("epicsThreadSetPriority failed\n");
383 }
384 
386  unsigned int priority, unsigned *pPriorityJustBelow)
387 {
388  unsigned newPriority = priority - 1;
389  if (newPriority <= 99) {
390  *pPriorityJustBelow = newPriority;
392  }
394 }
395 
397  unsigned int priority, unsigned *pPriorityJustAbove)
398 {
399  unsigned newPriority = priority + 1;
400 
401  newPriority = priority + 1;
402  if (newPriority <= 99) {
403  *pPriorityJustAbove = newPriority;
405  }
407 }
408 
410 {
411  return((id1==id2) ? 1 : 0);
412 }
413 
415 {
416  int tid = (int)id;
417  return((int)taskIsSuspended(tid));
418 }
419 
420 void epicsThreadSleep(double seconds)
421 {
422  STATUS status;
423  int ticks;
424 
425  if (seconds > 0.0) {
426  seconds *= sysClkRateGet();
427  seconds += 0.99999999; /* 8 9s here is optimal */
428  ticks = (seconds >= INT_MAX) ? INT_MAX : (int) seconds;
429  }
430  else { /* seconds <= 0 or NAN */
431  ticks = 0;
432  }
433  status = taskDelay(ticks);
434  if(status) errlogPrintf("epicsThreadSleep\n");
435 }
436 
438 {
439  return((epicsThreadId)taskIdSelf());
440 }
441 
443 {
444  int tid = taskNameToId((char *)name);
445  return((epicsThreadId)(tid==ERROR?0:tid));
446 }
447 
448 const char *epicsThreadGetNameSelf(void)
449 {
450  return taskName(taskIdSelf());
451 }
452 
453 void epicsThreadGetName (epicsThreadId id, char *name, size_t size)
454 {
455  int tid = (int)id;
456  strncpy(name,taskName(tid),size-1);
457  name[size-1] = '\0';
458 }
459 
461 {
462  int noTasks = 0;
463  int i;
464  int result;
465 
466  result = semTake(epicsThreadListMutex, WAIT_FOREVER);
467  assert(result == OK);
468  while (noTasks == 0) {
469  noTasks = taskIdListGet(taskIdList, taskIdListSize);
470  if (noTasks == taskIdListSize) {
471  taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int));
472  assert(taskIdList);
474  noTasks = 0;
475  }
476  }
477  for (i = 0; i < noTasks; i++) {
478  func ((epicsThreadId)taskIdList[i]);
479  }
480  semGive(epicsThreadListMutex);
481 }
482 
483 void epicsThreadShowAll(unsigned int level)
484 {
485  taskShow(0,2);
486 }
487 
488 void epicsThreadShow(epicsThreadId id, unsigned int level)
489 {
490  int tid = (int)id;
491 
492  if (level > 1) level = 1;
493  if (tid)
494  taskShow(tid, level);
495 }
496 
497 /* The following algorithm was thought of by Andrew Johnson APS/ASD .
498  * The basic idea is to use a single vxWorks task variable.
499  * The task variable is papTSD, which is an array of pointers to the TSD
500  * The array size is equal to the number of epicsThreadPrivateIds created + 1
501  * when epicsThreadPrivateSet is called.
502  * Until the first call to epicsThreadPrivateCreate by a application papTSD=0
503  * After first call papTSD[0] is value of nepicsThreadPrivate when
504  * epicsThreadPrivateSet was last called by the thread. This is also
505  * the value of epicsThreadPrivateId.
506  * The algorithm allows for epicsThreadPrivateCreate being called after
507  * the first call to epicsThreadPrivateSet.
508  */
510 {
511  static int lock = 0;
513 
514  epicsThreadInit();
515  /*lock is necessary because ++nepicsThreadPrivate may not be indivisible*/
516  while(!vxTas(&lock)) taskDelay(1);
517  id = (epicsThreadPrivateId)++nepicsThreadPrivate;
518  lock = 0;
519  return(id);
520 }
521 
523 {
524  /*nothing to delete */
525  return;
526 }
527 
528 /*
529  * No mutex is necessary here because by definition
530  * thread variables are local to a single thread.
531  */
533 {
534  int int_id = (int)id;
535  int nepicsThreadPrivateOld = 0;
536 
537  if (papTSD) nepicsThreadPrivateOld = (int)papTSD[0];
538 
539  if (!papTSD || nepicsThreadPrivateOld < int_id) {
540  void **papTSDold = papTSD;
541  int i;
542 
543  papTSD = callocMustSucceed(int_id + 1,sizeof(void *),
544  "epicsThreadPrivateSet");
545  papTSD[0] = (void *)(int_id);
546  for (i = 1; i <= nepicsThreadPrivateOld; i++) {
547  papTSD[i] = papTSDold[i];
548  }
549  free (papTSDold);
550  }
551  papTSD[int_id] = pvt;
552 }
553 
555 {
556  int int_id = (int)id;
557 
558  /*
559  * If epicsThreadPrivateGet() is called before epicsThreadPrivateSet()
560  * for any particular thread, the value returned is always NULL.
561  */
562  if ( papTSD && int_id <= (int) papTSD[0] ) {
563  return papTSD[int_id];
564  }
565  return NULL;
566 }
567 
569 {
570  double HZ = sysClkRateGet ();
571  return 1.0 / HZ;
572 }
573 
574 LIBCOM_API int epicsThreadGetCPUs(void)
575 {
576  return 1;
577 }
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(epicsThreadStackSizeClass stackSizeClass)
Definition: osdThread.c:466
LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadHighestPriorityLevelBelow(unsigned int priority, unsigned *pPriorityJustBelow)
Definition: osdThread.c:740
LIBCOM_API int epicsThreadGetCPUs(void)
Definition: osdThread.c:990
LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void)
Definition: osdThread.c:934
pthread_t tid
Definition: osdThread.h:26
int sysClkRateGet(void)
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
LIBCOM_API void epicsStdCall epicsThreadExitMain(void)
Definition: osdThread.c:683
pvac::PutEvent result
Definition: clientSync.cpp:117
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
epicsThreadBooleanStatus
Definition: epicsThread.h:93
epicsMutexId lock
Definition: osiClockTime.c:37
#define EPICS_THREAD_ONCE_DONE
pvd::Status status
An EPICS-specific replacement for ANSI C&#39;s assert.
#define PREPARE_JOIN(tid, joinable)
Definition: osdThread.c:225
LIBCOM_API void epicsExitCallAtThreadExits(void)
Internal routine that runs the registered thread exit routines.
Definition: epicsExit.c:121
int i
Definition: scan.c:967
#define THREAD_SEM_FLAGS
Definition: osdThread.c:108
EPICSTHREADFUNC funptr
Definition: osdThread.c:50
LIBCOM_API void epicsThreadRealtimeLock(void)
Definition: osdThread.c:425
LIBCOM_API double epicsStdCall epicsThreadSleepQuantum()
Query a value approximating the OS timer/scheduler resolution.
Definition: osdThread.c:981
void(* EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id)
Definition: epicsThread.h:302
#define NULL
Definition: catime.c:38
LIBCOM_API epicsThreadBooleanStatus epicsStdCall epicsThreadLowestPriorityLevelAbove(unsigned int priority, unsigned *pPriorityJustAbove)
Definition: osdThread.c:757
unsigned int joinable
Definition: epicsThread.h:158
LIBCOM_API void epicsStdCall epicsThreadPrivateSet(epicsThreadPrivateId id, void *value)
Definition: osdThread.c:961
#define TASK_FLAGS
Definition: osdThread.c:251
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg)
Definition: osdThread.c:487
A doubly-linked list library.
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
epicsThreadStackSizeClass
Definition: epicsThread.h:89
struct epicsThreadPrivateOSD * epicsThreadPrivateId
Definition: epicsThread.h:328
void epicsThreadEntry(EPICSTHREADFUNC func, void *parm)
Definition: osdThread.c:230
#define epicsThreadPriorityMax
Definition: epicsThread.h:71
LIBCOM_API void epicsStdCall * epicsThreadPrivateGet(epicsThreadPrivateId id)
Definition: osdThread.c:973
epicsThreadId epicsThreadCreateOpt(const char *name, EPICSTHREADFUNC funptr, void *parm, const epicsThreadOpts *opts)
Allocate and start a new OS thread.
Definition: osdThread.c:529
unsigned int stackSize
Definition: epicsThread.h:154
void * parm
Definition: osdThread.c:51
LIBCOM_API unsigned int epicsStdCall epicsThreadGetPrioritySelf(void)
Definition: osdThread.c:707
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetId(const char *name)
Definition: osdThread.c:826
void atRebootRegister(void)
Definition: atReboot.cpp:22
Extended replacement for the Posix exit and atexit routines.
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void)
Definition: osdThread.c:664
LIBCOM_API void epicsStdCall epicsThreadShow(epicsThreadId showThread, unsigned int level)
Definition: osdThread.c:903
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
LIBCOM_API int epicsStdCall epicsThreadIsSuspended(epicsThreadId pthreadInfo)
Definition: osdThread.c:784
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
LIBCOM_API void epicsStdCall epicsThreadResume(epicsThreadOSD *pthreadInfo)
Definition: osdThread.c:676
LIBCOM_API void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func)
Definition: osdThread.c:864
const ChannelProviderRegistry::factoryfn_t fn
#define ARCH_STACK_FACTOR
Definition: osdThread.c:56
#define ID_LIST_CHUNK
Definition: osdThread.c:66
char name[1]
Definition: osdThread.h:40
void done(int k)
Definition: antelope.c:77
unsigned int priority
Definition: epicsThread.h:150
LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level)
Definition: osdThread.c:883
#define EPICS_THREAD_OPTS_INIT
Definition: epicsThread.h:167
LIBCOM_API void cantProceed(const char *msg,...)
Definition: cantProceed.c:54
LIBCOM_API unsigned int epicsStdCall epicsThreadGetPriority(epicsThreadId pthreadInfo)
Definition: osdThread.c:701
void epicsThreadMustJoin(epicsThreadId id)
Definition: osdThread.c:632
Routines for code that can&#39;t continue or return after an error.
LIBCOM_API void osdThreadHooksRun(epicsThreadId id)
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void)
Definition: osdThread.c:810
LIBCOM_API void epicsStdCall epicsThreadSetPriority(epicsThreadId pthreadInfo, unsigned int priority)
Definition: osdThread.c:713
C++ and C descriptions for a thread.
LIBCOM_API void epicsStdCall epicsThreadPrivateDelete(epicsThreadPrivateId id)
Definition: osdThread.c:950
int taskIdListSize
Definition: osdThread.c:68
#define epicsThreadPriorityMin
Definition: epicsThread.h:72
void(* EPICSTHREADFUNC)(void *parm)
Definition: epicsThread.h:66
LIBCOM_API const char epicsStdCall * epicsThreadGetNameSelf()
Definition: osdThread.c:846
LIBCOM_API int epicsStdCall epicsThreadIsEqual(epicsThreadId p1, epicsThreadId p2)
Definition: osdThread.c:776
LIBCOM_API void epicsStdCall epicsThreadGetName(epicsThreadId pthreadInfo, char *name, size_t size)
Definition: osdThread.c:857
#define ALLOT_JOIN(tid)
Definition: osdThread.c:49
#define AWAIT_JOIN(tid)
Definition: osdThread.c:226