This is Unofficial EPICS BASE Doxygen Site
epicsThread.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, 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 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 //
10 // Author: Jeff Hill
11 //
12 
13 #include <exception>
14 #include <typeinfo>
15 
16 #include <stdio.h>
17 #include <stddef.h>
18 #include <float.h>
19 #include <string.h>
20 
21 // The following is required for Solaris builds
22 #undef __EXTENSIONS__
23 
24 #include "epicsAlgorithm.h"
25 #include "epicsTime.h"
26 #include "epicsThread.h"
27 #include "epicsAssert.h"
28 #include "epicsGuard.h"
29 #include "errlog.h"
30 
31 using namespace std;
32 
34  const char * name, unsigned int priority, unsigned int stackSize,
35  EPICSTHREADFUNC funptr,void * parm )
36 {
38  opts.priority = priority;
39  opts.stackSize = stackSize;
40  opts.joinable = 0;
41 
42  return epicsThreadCreateOpt(name, funptr, parm, &opts);
43 }
44 
45 epicsThreadRunable::~epicsThreadRunable () {}
46 void epicsThreadRunable::run () {}
47 void epicsThreadRunable::show ( unsigned int ) const {}
48 
50  public std :: exception {
51 public:
52  const char * what () const throw ();
53 };
54 
55 const char * epicsThread ::
57 {
58  return "unable to create thread";
59 }
60 
61 void epicsThread :: printLastChanceExceptionMessage (
62  const char * pExceptionTypeName,
63  const char * pExceptionContext )
64 {
65  char date[64];
66  try {
67  epicsTime cur = epicsTime :: getCurrent ();
68  cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
69  }
70  catch ( ... ) {
71  strcpy ( date, "<UKN DATE>" );
72  }
73  char name [128];
74  epicsThreadGetName ( this->id, name, sizeof ( name ) );
75  errlogPrintf (
76  "epicsThread: Unexpected C++ exception \"%s\" "
77  "with type \"%s\" in thread \"%s\" at %s\n",
78  pExceptionContext, pExceptionTypeName, name, date );
79  errlogFlush ();
80  // This behavior matches the C++ implementation when an exception
81  // isn't handled by the thread code. Users can install their own
82  // application-specific unexpected handler if preferred.
83  std::unexpected ();
84 }
85 
86 extern "C" void epicsThreadCallEntryPoint ( void * pPvt )
87 {
88  epicsThread * pThread =
89  static_cast <epicsThread *> ( pPvt );
90  bool threadDestroyed = false;
91  try {
92  pThread->pThreadDestroyed = & threadDestroyed;
93  if ( pThread->beginWait () ) {
94  pThread->runable.run ();
95  // The run() routine may have destroyed the epicsThread
96  // object by now; pThread can only be used below here
97  // when the threadDestroyed flag is false.
98  }
99  }
100  catch ( const epicsThread::exitException & ) {
101  }
102  catch ( std :: exception & except ) {
103  if ( ! threadDestroyed ) {
104  pThread->printLastChanceExceptionMessage (
105  typeid ( except ).name (), except.what () );
106  }
107  }
108  catch ( ... ) {
109  if ( ! threadDestroyed ) {
110  pThread->printLastChanceExceptionMessage (
111  "catch ( ... )", "Non-standard C++ exception" );
112  }
113  }
114  if ( ! threadDestroyed ) {
115  epicsGuard < epicsMutex > guard ( pThread->mutex );
116  pThread->pThreadDestroyed = NULL;
117  pThread->terminated = true;
118  pThread->exitEvent.signal ();
119  // After the terminated flag is set and guard's destructor
120  // releases the lock, pThread must never be used again.
121  }
122 }
123 
124 bool epicsThread::beginWait () throw ()
125 {
126  epicsGuard < epicsMutex > guard ( this->mutex );
127  while ( ! this->begin && ! this->cancel ) {
128  epicsGuardRelease < epicsMutex > unguard ( guard );
129  this->event.wait ();
130  }
131  return this->begin && ! this->cancel;
132 }
133 
134 void epicsThread::exit ()
135 {
136  throw exitException ();
137 }
138 
139 void epicsThread::exitWait () throw ()
140 {
141  bool success = this->exitWait ( DBL_MAX );
142  assert ( success );
143 }
144 
145 bool epicsThread::exitWait ( const double delay ) throw ()
146 {
147  try {
148  // When called (usually by a destructor) in the context of
149  // the managed thread we can't wait for the thread to exit.
150  // Set the threadDestroyed flag and return success.
151  if ( this->isCurrentThread() ) {
152  if ( this->pThreadDestroyed ) {
153  *this->pThreadDestroyed = true;
154  }
155  bool j;
156  {
157  epicsGuard < epicsMutex > guard ( this->mutex );
158  j = joined;
159  joined = true;
160  }
161  if(!j) {
162  epicsThreadMustJoin(this->id);
163  }
164  return true;
165  }
166  epicsTime exitWaitBegin = epicsTime::getCurrent ();
167  double exitWaitElapsed = 0.0;
168  epicsGuard < epicsMutex > guard ( this->mutex );
169  this->cancel = true;
170  while ( ! this->terminated && exitWaitElapsed < delay ) {
171  epicsGuardRelease < epicsMutex > unguard ( guard );
172  this->event.signal ();
173  this->exitEvent.wait ( delay - exitWaitElapsed );
174  epicsTime current = epicsTime::getCurrent ();
175  exitWaitElapsed = current - exitWaitBegin;
176  }
177  if(this->terminated && !joined) {
178  joined = true;
179 
180  epicsGuardRelease < epicsMutex > unguard ( guard );
181  epicsThreadMustJoin(this->id);
182  }
183  }
184  catch ( std :: exception & except ) {
185  errlogPrintf (
186  "epicsThread::exitWait(): Unexpected exception "
187  " \"%s\"\n",
188  except.what () );
189  epicsThreadSleep ( epicsMin ( delay, 5.0 ) );
190  }
191  catch ( ... ) {
192  errlogPrintf (
193  "Non-standard unexpected exception in "
194  "epicsThread::exitWait()\n" );
195  epicsThreadSleep ( epicsMin ( delay, 5.0 ) );
196  }
197  // the event mechanism is used for other purposes
198  this->event.signal ();
199  return this->terminated;
200 }
201 
202 epicsThread::epicsThread (
203  epicsThreadRunable & runableIn, const char * pName,
204  unsigned stackSize, unsigned priority ) :
205  runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ),
206  begin ( false ), cancel ( false ), terminated ( false ),
207  joined ( false )
208 {
210  opts.stackSize = stackSize;
211  opts.priority = priority;
212  opts.joinable = 1;
213 
214  this->id = epicsThreadCreateOpt(
216  static_cast < void * > ( this ),
217  &opts);
218  if ( ! this->id ) {
219  throw unableToCreateThread ();
220  }
221 }
222 
223 epicsThread::~epicsThread () throw ()
224 {
225  while ( ! this->exitWait ( 10.0 ) ) {
226  char nameBuf [256];
227  this->getName ( nameBuf, sizeof ( nameBuf ) );
228  fprintf ( stderr,
229  "epicsThread::~epicsThread(): \"%s\" blocking for thread \"%s\" to exit\n",
230  getNameSelf(), nameBuf );
231  fprintf ( stderr,
232  "was epicsThread object destroyed before thread exit ?\n");
233  }
234 }
235 
236 void epicsThread::start () throw ()
237 {
238  {
239  epicsGuard < epicsMutex > guard ( this->mutex );
240  this->begin = true;
241  }
242  this->event.signal ();
243 }
244 
245 bool epicsThread::isCurrentThread () const throw ()
246 {
247  return ( epicsThreadGetIdSelf () == this->id );
248 }
249 
250 void epicsThread::resume () throw ()
251 {
252  epicsThreadResume ( this->id );
253 }
254 
255 void epicsThread::getName ( char *name, size_t size ) const throw ()
256 {
257  epicsThreadGetName ( this->id, name, size );
258 }
259 
260 epicsThreadId epicsThread::getId () const throw ()
261 {
262  return this->id;
263 }
264 
265 unsigned int epicsThread::getPriority () const throw ()
266 {
267  return epicsThreadGetPriority (this->id);
268 }
269 
270 void epicsThread::setPriority (unsigned int priority) throw ()
271 {
272  epicsThreadSetPriority (this->id, priority);
273 }
274 
275 bool epicsThread::priorityIsEqual (const epicsThread &otherThread) const throw ()
276 {
277  if ( epicsThreadIsEqual (this->id, otherThread.id) ) {
278  return true;
279  }
280  return false;
281 }
282 
283 bool epicsThread::isSuspended () const throw ()
284 {
285  if ( epicsThreadIsSuspended (this->id) ) {
286  return true;
287  }
288  return false;
289 }
290 
291 bool epicsThread::operator == (const epicsThread &rhs) const throw ()
292 {
293  return (this->id == rhs.id);
294 }
295 
296 void epicsThread::suspendSelf () throw ()
297 {
299 }
300 
301 void epicsThread::sleep (double seconds) throw ()
302 {
303  epicsThreadSleep (seconds);
304 }
305 
306 const char *epicsThread::getNameSelf () throw ()
307 {
308  return epicsThreadGetNameSelf ();
309 }
310 
311 bool epicsThread::isOkToBlock () throw ()
312 {
313  return epicsThreadIsOkToBlock() != 0;
314 }
315 
316 void epicsThread::setOkToBlock(bool isOkToBlock) throw ()
317 {
318  epicsThreadSetOkToBlock(static_cast<int>(isOkToBlock));
319 }
320 
321 void epicsThreadPrivateBase::throwUnableToCreateThreadPrivate ()
322 {
323  throw epicsThreadPrivateBase::unableToCreateThreadPrivate ();
324 }
325 
326 void epicsThread :: show ( unsigned level ) const throw ()
327 {
328  ::printf ( "epicsThread at %p\n", this->id );
329  if ( level > 0u ) {
330  epicsThreadShow ( this->id, level - 1 );
331  if ( level > 1u ) {
332  ::printf ( "pThreadDestroyed = %p\n", this->pThreadDestroyed );
333  ::printf ( "begin = %c, cancel = %c, terminated = %c\n",
334  this->begin ? 'T' : 'F',
335  this->cancel ? 'T' : 'F',
336  this->terminated ? 'T' : 'F' );
337  this->runable.show ( level - 2u );
338  this->mutex.show ( level - 2u );
339  ::printf ( "general purpose event\n" );
340  this->event.show ( level - 2u );
341  ::printf ( "exit event\n" );
342  this->exitEvent.show ( level - 2u );
343  }
344  }
345 }
346 
347 extern "C" {
348  static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT;
350  static const int okToBlockNo = 0;
351  static const int okToBlockYes = 1;
352 
353  static void epicsThreadOnceIdInit(void *)
354  {
355  okToBlockPrivate = epicsThreadPrivateCreate();
356  }
357 
358  int epicsStdCall epicsThreadIsOkToBlock(void)
359  {
360  const int *pokToBlock;
361  epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
362  pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate);
363  return (pokToBlock ? *pokToBlock : 0);
364  }
365 
366  void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
367  {
368  const int *pokToBlock;
369  epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL);
370  pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo;
371  epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock);
372  }
373 
375  const char *name, unsigned int priority, unsigned int stackSize,
376  EPICSTHREADFUNC funptr,void *parm)
377  {
379  name, priority, stackSize, funptr, parm );
380  assert ( id );
381  return id;
382  }
383 } // extern "C"
384 
385 static epicsThreadId initMainThread(void) {
388  return main;
389 }
390 
391 // Ensure the main thread gets a unique ID and allows blocking I/O
392 epicsThreadId epicsThreadMainId = initMainThread();
void errlogFlush(void)
Definition: errlog.c:529
void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock)
epicsThreadPrivateId okToBlockPrivate
epicsThreadId epicsStdCall epicsThreadMustCreate(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr, void *parm)
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
LIBCOM_API unsigned int epicsStdCall epicsThreadGetPriority(epicsThreadId id)
Definition: osdThread.c:701
An EPICS-specific replacement for ANSI C&#39;s assert.
LIBCOM_API void *epicsStdCall epicsThreadPrivateGet(epicsThreadPrivateId)
Definition: osdThread.c:973
LIBCOM_API const char *epicsStdCall epicsThreadGetNameSelf(void)
Definition: osdThread.c:846
#define printf
Definition: epicsStdio.h:41
Definition: memory.hpp:41
#define NULL
Definition: catime.c:38
LIBCOM_API void epicsStdCall epicsThreadShow(epicsThreadId id, unsigned int level)
Definition: osdThread.c:903
const char * what() const
Definition: epicsThread.cpp:56
LIBCOM_API int epicsStdCall epicsThreadIsEqual(epicsThreadId id1, epicsThreadId id2)
Definition: osdThread.c:776
unsigned int joinable
Definition: epicsThread.h:158
epicsTime begin
Definition: caConnTest.cpp:22
epicsThreadId epicsStdCall epicsThreadCreate(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr, void *parm)
Definition: epicsThread.cpp:33
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void)
Definition: osdThread.c:664
epicsMutex mutex
Definition: pvAccess.cpp:71
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
unsigned int stackSize
Definition: epicsThread.h:154
const T & epicsMin(const T &a, const T &b)
LIBCOM_API void epicsStdCall epicsThreadPrivateSet(epicsThreadPrivateId, void *)
Definition: osdThread.c:961
LIBCOM_API void epicsStdCall epicsThreadResume(epicsThreadId id)
Definition: osdThread.c:423
LIBCOM_API void epicsStdCall epicsThreadGetName(epicsThreadId id, char *name, size_t size)
Definition: osdThread.c:857
int main(int argc, char **argv)
Definition: acctstMain.c:17
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
void date(const char *format)
int epicsStdCall epicsThreadIsOkToBlock(void)
unsigned int priority
Definition: epicsThread.h:150
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds)
Block the calling thread for at least the specified time.
Definition: osdThread.c:790
#define EPICS_THREAD_OPTS_INIT
Definition: epicsThread.h:167
Contains a few templates out of the C++ standard header algorithm.
LIBCOM_API void epicsThreadMustJoin(epicsThreadId id)
Definition: osdThread.c:632
LIBCOM_API epicsThreadId epicsThreadCreateOpt(const char *name, EPICSTHREADFUNC funptr, void *parm, const epicsThreadOpts *opts)
Allocate and start a new OS thread.
Definition: osdThread.c:529
LIBCOM_API int epicsStdCall epicsThreadIsSuspended(epicsThreadId id)
Definition: osdThread.c:784
#define stderr
Definition: epicsStdio.h:32
EPICS time-stamps (epicsTimeStamp), epicsTime C++ class and C functions for handling wall-clock times...
void epicsThreadCallEntryPoint(void *pPvt)
Definition: epicsThread.cpp:86
C++ and C descriptions for a thread.
LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void)
Definition: osdThread.c:934
epicsThreadId epicsThreadMainId
void(* EPICSTHREADFUNC)(void *parm)
Definition: epicsThread.h:66
LIBCOM_API void epicsStdCall epicsThreadSetPriority(epicsThreadId id, unsigned int priority)
Definition: osdThread.c:713
bool operator==(const epics::pvData::shared_vector< A > &a, const epics::pvData::shared_vector< B > &b)
Definition: sharedVector.h:967
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void)
Definition: osdThread.c:810