This is Unofficial EPICS BASE Doxygen Site
epicsMutex.cpp
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 * EPICS BASE Versions 3.13.7
7 * and higher are distributed subject to a Software License Agreement found
8 * in file LICENSE that is included with this distribution.
9 \*************************************************************************/
10 /* epicsMutex.cpp */
11 /* Author: Marty Kraimer and Jeff Hill Date: 03APR01 */
12 
13 /*
14  * NOTES:
15  * 1) LOG_LAST_OWNER feature is normally commented out because
16  * it slows down the system at run time, anfd because its not
17  * currently safe to convert a thread id to a thread name because
18  * the thread may have exited making the thread id invalid.
19  */
20 
21 #include <new>
22 
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include "epicsStdio.h"
29 #include "epicsThread.h"
30 #include "valgrind/valgrind.h"
31 #include "ellLib.h"
32 #include "errlog.h"
33 #include "epicsMutex.h"
34 #include "epicsThread.h"
35 
36 static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT;
37 static ELLLIST mutexList;
38 static ELLLIST freeList;
39 
43 # ifdef LOG_LAST_OWNER
44  epicsThreadId lastOwner;
45 # endif
46  const char *pFileName;
47  int lineno;
48 };
49 
50 static epicsMutexOSD * epicsMutexGlobalLock;
51 
52 
53 // vxWorks 5.4 gcc fails during compile when I use std::exception
54 using namespace std;
55 
56 // exception payload
57 class epicsMutex::mutexCreateFailed : public exception
58 {
59  const char * what () const throw ();
60 };
61 
62 const char * epicsMutex::mutexCreateFailed::what () const throw ()
63 {
64  return "epicsMutex::mutexCreateFailed()";
65 }
66 
67 // exception payload
68 class epicsMutex::invalidMutex : public exception
69 {
70  const char * what () const throw ();
71 };
72 
73 const char * epicsMutex::invalidMutex::what () const throw ()
74 {
75  return "epicsMutex::invalidMutex()";
76 }
77 
78 static void epicsMutexOsiInit(void *) {
79  ellInit(&mutexList);
80  ellInit(&freeList);
81  VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0);
82  epicsMutexGlobalLock = epicsMutexOsdCreate();
83 }
84 
86  const char *pFileName,int lineno)
87 {
88  epicsMutexOSD * id;
89 
90  epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL);
91 
92  id = epicsMutexOsdCreate();
93  if(!id) {
94  return 0;
95  }
96  epicsMutexLockStatus lockStat =
97  epicsMutexOsdLock(epicsMutexGlobalLock);
98  assert ( lockStat == epicsMutexLockOK );
99  epicsMutexParm *pmutexNode =
100  reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) );
101  if(pmutexNode) {
102  ellDelete(&freeList,&pmutexNode->node);
103  VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
104  } else {
105  pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) );
106  }
107  VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm));
108  pmutexNode->id = id;
109 # ifdef LOG_LAST_OWNER
110  pmutexNode->lastOwner = 0;
111 # endif
112  pmutexNode->pFileName = pFileName;
113  pmutexNode->lineno = lineno;
114  ellAdd(&mutexList,&pmutexNode->node);
115  epicsMutexOsdUnlock(epicsMutexGlobalLock);
116  return(pmutexNode);
117 }
118 
120  const char *pFileName,int lineno)
121 {
122  epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno);
123  assert(id);
124  return(id );
125 }
126 
127 void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode)
128 {
129  epicsMutexLockStatus lockStat =
130  epicsMutexOsdLock(epicsMutexGlobalLock);
131  assert ( lockStat == epicsMutexLockOK );
132  ellDelete(&mutexList,&pmutexNode->node);
133  epicsMutexOsdDestroy(pmutexNode->id);
134  VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode);
135  VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node));
136  ellAdd(&freeList,&pmutexNode->node);
137  epicsMutexOsdUnlock(epicsMutexGlobalLock);
138 }
139 
140 void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
141 {
142  epicsMutexOsdUnlock(pmutexNode->id);
143 }
144 
146  epicsMutexId pmutexNode)
147 {
149  epicsMutexOsdLock(pmutexNode->id);
150 # ifdef LOG_LAST_OWNER
151  if ( status == epicsMutexLockOK ) {
152  pmutexNode->lastOwner = epicsThreadGetIdSelf();
153  }
154 # endif
155  return status;
156 }
157 
159  epicsMutexId pmutexNode)
160 {
162  epicsMutexOsdTryLock(pmutexNode->id);
163 # ifdef LOG_LAST_OWNER
164  if ( status == epicsMutexLockOK ) {
165  pmutexNode->lastOwner = epicsThreadGetIdSelf();
166  }
167 # endif
168  return status;
169 }
170 
171 /* Empty the freeList.
172  * Called from epicsExit.c, but not via epicsAtExit()
173  * to avoid the possibility of a circular reference.
174  */
175 extern "C"
177 {
178  ELLNODE *cur;
179  epicsMutexLockStatus lockStat =
180  epicsMutexOsdLock(epicsMutexGlobalLock);
181  assert ( lockStat == epicsMutexLockOK );
182 
183  while((cur=ellGet(&freeList))!=NULL) {
184  VALGRIND_MEMPOOL_FREE(&freeList, cur);
185  free(cur);
186  }
187 
188  epicsMutexOsdUnlock(epicsMutexGlobalLock);
189 }
190 
191 void epicsStdCall epicsMutexShow(
192  epicsMutexId pmutexNode, unsigned int level)
193 {
194 # ifdef LOG_LAST_OWNER
195  char threadName [255];
196  if ( pmutexNode->lastOwner ) {
197 # error currently not safe to fetch name for stale thread
198  epicsThreadGetName ( pmutexNode->lastOwner,
199  threadName, sizeof ( threadName ) );
200  }
201  else {
202  strcpy ( threadName, "<not used>" );
203  }
204  printf("epicsMutexId %p last owner \"%s\" source %s line %d\n",
205  (void *)pmutexNode, threadName,
206  pmutexNode->pFileName, pmutexNode->lineno);
207 # else
208  printf("epicsMutexId %p source %s line %d\n",
209  (void *)pmutexNode, pmutexNode->pFileName,
210  pmutexNode->lineno);
211 # endif
212  if ( level > 0 ) {
213  epicsMutexOsdShow(pmutexNode->id,level-1);
214  }
215 }
216 
217 void epicsStdCall epicsMutexShowAll(int onlyLocked,unsigned int level)
218 {
219  epicsMutexParm *pmutexNode;
220 
221  if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT)
222  return;
223 
224  printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n",
225  ellCount(&mutexList),ellCount(&freeList));
226  epicsMutexLockStatus lockStat =
227  epicsMutexOsdLock(epicsMutexGlobalLock);
228  assert ( lockStat == epicsMutexLockOK );
229  pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) );
230  while(pmutexNode) {
231  if(onlyLocked) {
233  status = epicsMutexOsdTryLock(pmutexNode->id);
234  if(status==epicsMutexLockOK) {
235  epicsMutexOsdUnlock(pmutexNode->id);
236  pmutexNode =
237  reinterpret_cast < epicsMutexParm * >
238  ( ellNext(&pmutexNode->node) );
239  continue;
240  }
241  }
242  epicsMutexShow(pmutexNode, level);
243  pmutexNode =
244  reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) );
245  }
246  epicsMutexOsdUnlock(epicsMutexGlobalLock);
247 }
248 
249 #if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8)
250 epicsMutex :: epicsMutex () :
251  id ( epicsMutexCreate () )
252 {
253  if ( this->id == 0 ) {
254  throw mutexCreateFailed ();
255  }
256 }
257 #endif
258 
259 epicsMutex :: epicsMutex ( const char *pFileName, int lineno ) :
261 {
262  if ( this->id == 0 ) {
263  throw mutexCreateFailed ();
264  }
265 }
266 
267 epicsMutex ::~epicsMutex ()
268 {
269  epicsMutexDestroy ( this->id );
270 }
271 
272 void epicsMutex::lock ()
273 {
275  if ( status != epicsMutexLockOK ) {
276  throw invalidMutex ();
277  }
278 }
279 
280 bool epicsMutex::tryLock ()
281 {
283  if ( status == epicsMutexLockOK ) {
284  return true;
285  }
286  else if ( status != epicsMutexLockTimeout ) {
287  throw invalidMutex ();
288  }
289  return false;
290 }
291 
292 void epicsMutex::unlock ()
293 {
294  epicsMutexUnlock ( this->id );
295 }
296 
297 void epicsMutex :: show ( unsigned level ) const
298 {
299  epicsMutexShow ( this->id, level );
300 }
301 
302 static epicsThreadPrivate < epicsDeadlockDetectMutex >
303  * pCurrentMutexLevel = 0;
304 
305 static epicsThreadOnceId epicsDeadlockDetectMutexInit = EPICS_THREAD_ONCE_INIT;
306 
307 extern "C"
309 {
310  pCurrentMutexLevel = new epicsThreadPrivate < epicsDeadlockDetectMutex > ();
311 }
312 
313 epicsDeadlockDetectMutex::
314  epicsDeadlockDetectMutex ( hierarchyLevel_t level ) :
315  hierarchyLevel ( level ), pPreviousLevel ( 0 )
316 {
317  epicsThreadOnce ( & epicsDeadlockDetectMutexInit,
319 }
320 
321 epicsDeadlockDetectMutex::~epicsDeadlockDetectMutex ()
322 {
323 }
324 
325 void epicsDeadlockDetectMutex::show ( unsigned level ) const
326 {
327  this->mutex.show ( level );
328 }
329 
331 {
332  epicsDeadlockDetectMutex * pPrev = pCurrentMutexLevel->get();
333  if ( pPrev && pPrev != this ) {
334  if ( pPrev->hierarchyLevel >= this->hierarchyLevel ) {
335  errlogPrintf ( "!!!! Deadlock Vulnerability Detected !!!! "
336  "at level %u and moving to level %u\n",
337  pPrev->hierarchyLevel,
338  this->hierarchyLevel );
339  }
340  }
341  this->mutex.lock ();
342  if ( pPrev && pPrev != this ) {
343  pCurrentMutexLevel->set ( this );
344  this->pPreviousLevel = pPrev;
345  }
346 }
347 
348 void epicsDeadlockDetectMutex::unlock ()
349 {
350  pCurrentMutexLevel->set ( this->pPreviousLevel );
351  this->mutex.unlock ();
352 }
353 
354 bool epicsDeadlockDetectMutex::tryLock ()
355 {
356  bool success = this->mutex.tryLock ();
357  if ( success ) {
358  this->pPreviousLevel = pCurrentMutexLevel->get();
359  pCurrentMutexLevel->set ( this );
360  }
361  return success;
362 }
363 
364 
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
#define ellCount(PLIST)
Report the number of nodes in a list.
Definition: ellLib.h:84
epicsMutexId lock
Definition: osiClockTime.c:37
void epicsStdCall epicsMutexDestroy(epicsMutexId pmutexNode)
Destroy an epicsMutex semaphore.
Definition: epicsMutex.cpp:127
pvd::Status status
#define printf
Definition: epicsStdio.h:41
ELLNODE * ellGet(ELLLIST *pList)
Deletes and returns the first node from a list.
Definition: ellLib.c:147
Definition: memory.hpp:41
#define NULL
Definition: catime.c:38
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
epicsMutexId epicsStdCall epicsMutexOsiMustCreate(const char *pFileName, int lineno)
Internal API, used by epicsMutexMustCreate().
Definition: epicsMutex.cpp:119
void epicsDeadlockDetectMutexInitFunc(void *)
Definition: epicsMutex.cpp:308
#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed)
Definition: valgrind.h:6470
A doubly-linked list library.
void ellAdd(ELLLIST *pList, ELLNODE *pNode)
Adds a node to the end of a list.
Definition: ellLib.c:24
#define ellNext(PNODE)
Find the next node in list.
Definition: ellLib.h:99
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
epicsMutex mutex
Definition: pvAccess.cpp:71
APIs for the epicsMutex mutual exclusion semaphore.
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
epicsMutexOSD * id
Definition: epicsMutex.cpp:42
LIBCOM_API void epicsStdCall epicsThreadGetName(epicsThreadId id, char *name, size_t size)
Definition: osdThread.c:857
#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size)
Definition: valgrind.h:6480
void epicsMutexCleanup(void)
Definition: epicsMutex.cpp:176
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
List node type.
Definition: ellLib.h:45
epicsMutexLockStatus
Definition: epicsMutex.h:51
epicsMutexLockStatus epicsStdCall epicsMutexLock(epicsMutexId pmutexNode)
Claim the semaphore, waiting until it&#39;s free if currently owned owned by a different thread...
Definition: epicsMutex.cpp:145
epicsMutexLockStatus epicsStdCall epicsMutexTryLock(epicsMutexId pmutexNode)
Similar to epicsMutexLock() except that the call returns immediately, with the return status indicati...
Definition: epicsMutex.cpp:158
#define ellInit(PLIST)
Initialize a list type.
Definition: ellLib.h:76
#define epicsMutexCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:168
const char * pFileName
Definition: epicsMutex.cpp:46
void epicsStdCall epicsMutexShow(epicsMutexId pmutexNode, unsigned int level)
Display information about the semaphore.
Definition: epicsMutex.cpp:191
#define epicsMutexOsdUnlock(ID)
Definition: osdMutex.h:22
#define epicsMutexOsdLock(ID)
Definition: osdMutex.h:24
#define VALGRIND_MEMPOOL_FREE(pool, addr)
Definition: valgrind.h:6485
epicsMutexId epicsStdCall epicsMutexOsiCreate(const char *pFileName, int lineno)
Internal API, used by epicsMutexCreate().
Definition: epicsMutex.cpp:85
List header type.
Definition: ellLib.h:56
C++ and C descriptions for a thread.
void epicsStdCall epicsMutexShowAll(int onlyLocked, unsigned int level)
Display information about all epicsMutex semaphores.
Definition: epicsMutex.cpp:217
void ellDelete(ELLLIST *pList, ELLNODE *pNode)
Deletes a node from a list.
Definition: ellLib.c:75
#define ellFirst(PLIST)
Find the first node in list.
Definition: ellLib.h:89
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void)
Definition: osdThread.c:810