Asterisk - The Open Source Telephony Project  21.4.1
utils/lock.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief General Asterisk locking.
22  */
23 
24 /*** MODULEINFO
25  <support_level>core</support_level>
26  ***/
27 
28 #include "asterisk.h"
29 
30 #ifdef HAVE_MTX_PROFILE
31 /* profile mutex */
32 static int mtx_prof = -1;
33 static void __attribute__((constructor)) __mtx_init(void)
34 {
35  mtx_prof = ast_add_profile("mtx_lock_" __FILE__, 0);
36 }
37 #endif
38 
39 #include "asterisk/utils.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/manager.h"
42 
43 /* Allow direct use of pthread_mutex_* / pthread_cond_* */
44 #undef pthread_mutex_init
45 #undef pthread_mutex_destroy
46 #undef pthread_mutex_lock
47 #undef pthread_mutex_trylock
48 #undef pthread_mutex_t
49 #undef pthread_mutex_unlock
50 #undef pthread_cond_init
51 #undef pthread_cond_signal
52 #undef pthread_cond_broadcast
53 #undef pthread_cond_destroy
54 #undef pthread_cond_wait
55 #undef pthread_cond_timedwait
56 
57 #if defined(DEBUG_THREADS) || defined(DETECT_DEADLOCKS)
58 #define log_mutex_error(canlog, ...) \
59  do { \
60  if (canlog) { \
61  ast_log(LOG_ERROR, __VA_ARGS__); \
62  } else { \
63  fprintf(stderr, __VA_ARGS__); \
64  } \
65  } while (0)
66 #endif
67 
68 #if defined(DEBUG_THREADS) && defined(HAVE_BKTR)
69 static void __dump_backtrace(struct ast_bt *bt, int canlog)
70 {
71  char **strings;
72  ssize_t i;
73 
74  strings = backtrace_symbols(bt->addresses, bt->num_frames);
75 
76  for (i = 0; i < bt->num_frames; i++) {
77  log_mutex_error(canlog, "%s\n", strings[i]);
78  }
79 
80  ast_std_free(strings);
81 }
82 #endif /* defined(DEBUG_THREADS) && defined(HAVE_BKTR) */
83 
84 #ifdef DEBUG_THREADS
85 AST_MUTEX_DEFINE_STATIC(reentrancy_lock);
86 
87 static inline struct ast_lock_track *ast_get_reentrancy(struct ast_lock_track **plt,
88  struct ast_lock_track_flags *flags, int no_setup)
89 {
90  pthread_mutexattr_t reentr_attr;
91  struct ast_lock_track *lt;
92 
93  if (!flags->tracking || flags->setup) {
94  return *plt;
95  }
96 
97  pthread_mutex_lock(&reentrancy_lock.mutex);
98 
99  if (*plt) {
100  pthread_mutex_unlock(&reentrancy_lock.mutex);
101  return *plt;
102  }
103 
104  if (no_setup) {
105  pthread_mutex_unlock(&reentrancy_lock.mutex);
106  return NULL;
107  }
108 
109  lt = *plt = ast_std_calloc(1, sizeof(*lt));
110  if (!lt) {
111  fprintf(stderr, "%s: Failed to allocate lock tracking\n", __func__);
112 #if defined(DO_CRASH) || defined(THREAD_CRASH)
113  abort();
114 #else
115  pthread_mutex_unlock(&reentrancy_lock.mutex);
116  return NULL;
117 #endif
118  }
119 
120  pthread_mutexattr_init(&reentr_attr);
121  pthread_mutexattr_settype(&reentr_attr, AST_MUTEX_KIND);
122  pthread_mutex_init(&lt->reentr_mutex, &reentr_attr);
123  pthread_mutexattr_destroy(&reentr_attr);
124 
125  flags->setup = 1;
126  pthread_mutex_unlock(&reentrancy_lock.mutex);
127  return lt;
128 }
129 
130 static inline void delete_reentrancy_cs(struct ast_lock_track **plt)
131 {
132  struct ast_lock_track *lt;
133 
134  if (*plt) {
135  lt = *plt;
136  *plt = NULL;
137 
138  pthread_mutex_destroy(&lt->reentr_mutex);
139  ast_std_free(lt);
140  }
141 }
142 
143 #endif /* DEBUG_THREADS */
144 
145 int __ast_pthread_mutex_init(int tracking, const char *filename, int lineno, const char *func,
146  const char *mutex_name, ast_mutex_t *t)
147 {
148  int res;
149  pthread_mutexattr_t attr;
150 
151 #if defined(DEBUG_THREADS) && defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && \
152  defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
153  if ((t->mutex) != ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
154  int canlog = tracking && strcmp(filename, "logger.c");
155 
156  log_mutex_error(canlog, "%s line %d (%s): NOTICE: mutex '%s' is already initialized.\n",
157  filename, lineno, func, mutex_name);
158  DO_THREAD_CRASH;
159  return EBUSY;
160  }
161 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
162 
163 #if defined(DEBUG_THREADS) || defined(DETECT_DEADLOCKS)
164  t->track = NULL;
165  t->flags.tracking = tracking;
166  t->flags.setup = 0;
167 #endif /* DEBUG_THREADS */
168 
169  pthread_mutexattr_init(&attr);
170  pthread_mutexattr_settype(&attr, AST_MUTEX_KIND);
171  res = pthread_mutex_init(&t->mutex, &attr);
172  pthread_mutexattr_destroy(&attr);
173 
174  return res;
175 }
176 
177 int __ast_pthread_mutex_destroy(const char *filename, int lineno, const char *func,
178  const char *mutex_name, ast_mutex_t *t)
179 {
180  int res;
181 
182 #ifdef DEBUG_THREADS
183  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 1);
184  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
185 
186 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
187  if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
188  /* Don't try to uninitialize an uninitialized mutex
189  * This may have no effect on linux
190  * but it always generates a core on *BSD when
191  * linked with libpthread.
192  * This is not an error condition if the mutex is created on the fly.
193  */
194  log_mutex_error(canlog, "%s line %d (%s): NOTICE: mutex '%s' is uninitialized.\n",
195  filename, lineno, func, mutex_name);
196  DO_THREAD_CRASH;
197  res = EINVAL;
198  goto lt_cleanup;
199  }
200 #endif
201 
202  res = pthread_mutex_trylock(&t->mutex);
203  switch (res) {
204  case 0:
205  pthread_mutex_unlock(&t->mutex);
206  break;
207  case EINVAL:
208  log_mutex_error(canlog, "%s line %d (%s): Error: attempt to destroy invalid mutex '%s'.\n",
209  filename, lineno, func, mutex_name);
210  break;
211  case EBUSY:
212  log_mutex_error(canlog, "%s line %d (%s): Error: attempt to destroy locked mutex '%s'.\n",
213  filename, lineno, func, mutex_name);
214  if (lt) {
215  ast_reentrancy_lock(lt);
216  log_mutex_error(canlog, "%s line %d (%s): Error: '%s' was locked here.\n",
217  lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
218 #ifdef HAVE_BKTR
219  __dump_backtrace(&lt->backtrace[ROFFSET], canlog);
220 #endif
221  ast_reentrancy_unlock(lt);
222  }
223  break;
224  }
225 #endif /* DEBUG_THREADS */
226 
227  res = pthread_mutex_destroy(&t->mutex);
228 
229 #ifdef DEBUG_THREADS
230  if (res) {
231  log_mutex_error(canlog, "%s line %d (%s): Error destroying mutex %s: %s\n",
232  filename, lineno, func, mutex_name, strerror(res));
233  }
234 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
235 lt_cleanup:
236 #endif
237  if (lt) {
238  ast_reentrancy_lock(lt);
239  lt->file[0] = filename;
240  lt->lineno[0] = lineno;
241  lt->func[0] = func;
242  lt->reentrancy = 0;
243  lt->thread_id[0] = 0;
244 #ifdef HAVE_BKTR
245  memset(&lt->backtrace[0], 0, sizeof(lt->backtrace[0]));
246 #endif
247  ast_reentrancy_unlock(lt);
248  delete_reentrancy_cs(&t->track);
249  }
250 #endif /* DEBUG_THREADS */
251 
252  return res;
253 }
254 
255 int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
256  const char* mutex_name, ast_mutex_t *t)
257 {
258  int res;
259 
260 #if defined(DETECT_DEADLOCKS) || defined(DEBUG_THREADS)
261  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
262 #endif
263 
264 #ifdef DEBUG_THREADS
265  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
266  struct ast_bt *bt = NULL;
267 
268  if (lt) {
269 #ifdef HAVE_BKTR
270  struct ast_bt tmp;
271 
272  /* The implementation of backtrace() may have its own locks.
273  * Capture the backtrace outside of the reentrancy lock to
274  * avoid deadlocks. See ASTERISK-22455. */
275  ast_bt_get_addresses(&tmp);
276 
277  ast_reentrancy_lock(lt);
278  if (lt->reentrancy < AST_MAX_REENTRANCY) {
279  lt->backtrace[lt->reentrancy] = tmp;
280  bt = &lt->backtrace[lt->reentrancy];
281  }
282  ast_reentrancy_unlock(lt);
283 #endif
284  ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
285  }
286 #endif /* DEBUG_THREADS */
287 
288 #if defined(DETECT_DEADLOCKS)
289  {
290  time_t seconds = time(NULL);
291  time_t wait_time, reported_wait = 0;
292  do {
293 #ifdef HAVE_MTX_PROFILE
294  ast_mark(mtx_prof, 1);
295 #endif
296  res = pthread_mutex_trylock(&t->mutex);
297 #ifdef HAVE_MTX_PROFILE
298  ast_mark(mtx_prof, 0);
299 #endif
300  if (res == EBUSY) {
301  wait_time = time(NULL) - seconds;
302  if (wait_time > reported_wait && (wait_time % 5) == 0) {
303  log_mutex_error(canlog, "%s line %d (%s): Deadlock? waited %d sec for mutex '%s'?\n",
304  filename, lineno, func, (int) wait_time, mutex_name);
305 #ifdef DEBUG_THREADS
306  if (lt) {
307  ast_reentrancy_lock(lt);
308 #ifdef HAVE_BKTR
309  __dump_backtrace(&lt->backtrace[lt->reentrancy], canlog);
310 #endif
311  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
312  lt->file[ROFFSET], lt->lineno[ROFFSET],
313  lt->func[ROFFSET], mutex_name);
314 #ifdef HAVE_BKTR
315  __dump_backtrace(&lt->backtrace[ROFFSET], canlog);
316 #endif
317  ast_reentrancy_unlock(lt);
318  }
319 #endif
320  reported_wait = wait_time;
321  if ((int) wait_time < 10) { /* Only emit an event when a deadlock starts, not every 5 seconds */
322  /*** DOCUMENTATION
323  <managerEvent language="en_US" name="DeadlockStart">
324  <managerEventInstance class="EVENT_FLAG_SYSTEM">
325  <synopsis>Raised when a probable deadlock has started.
326  Delivery of this event is attempted but not guaranteed,
327  and could fail for example if the manager itself is deadlocked.
328  </synopsis>
329  <syntax>
330  <parameter name="Mutex">
331  <para>The mutex involved in the deadlock.</para>
332  </parameter>
333  </syntax>
334  </managerEventInstance>
335  </managerEvent>
336  ***/
337  manager_event(EVENT_FLAG_SYSTEM, "DeadlockStart",
338  "Mutex: %s\r\n",
339  mutex_name);
340  }
341  }
342  usleep(200);
343  }
344  } while (res == EBUSY);
345  }
346 #else /* !DETECT_DEADLOCKS || !DEBUG_THREADS */
347 #ifdef HAVE_MTX_PROFILE
348  ast_mark(mtx_prof, 1);
349  res = pthread_mutex_trylock(&t->mutex);
350  ast_mark(mtx_prof, 0);
351  if (res)
352 #endif
353  res = pthread_mutex_lock(&t->mutex);
354 #endif /* !DETECT_DEADLOCKS || !DEBUG_THREADS */
355 
356 #ifdef DEBUG_THREADS
357  if (lt && !res) {
358  ast_reentrancy_lock(lt);
359  if (lt->reentrancy < AST_MAX_REENTRANCY) {
360  lt->file[lt->reentrancy] = filename;
361  lt->lineno[lt->reentrancy] = lineno;
362  lt->func[lt->reentrancy] = func;
363  lt->thread_id[lt->reentrancy] = pthread_self();
364  lt->reentrancy++;
365  } else {
366  log_mutex_error(canlog, "%s line %d (%s): '%s' really deep reentrancy!\n",
367  filename, lineno, func, mutex_name);
368  }
369  ast_reentrancy_unlock(lt);
370  ast_mark_lock_acquired(t);
371  } else if (lt) {
372 #ifdef HAVE_BKTR
373  if (lt->reentrancy) {
374  ast_reentrancy_lock(lt);
375  bt = &lt->backtrace[lt->reentrancy-1];
376  ast_reentrancy_unlock(lt);
377  } else {
378  bt = NULL;
379  }
380 #endif
381  ast_remove_lock_info(t, bt);
382  }
383  if (res) {
384  log_mutex_error(canlog, "%s line %d (%s): Error obtaining mutex: %s\n",
385  filename, lineno, func, strerror(res));
386  DO_THREAD_CRASH;
387  }
388 #endif /* DEBUG_THREADS */
389 
390  return res;
391 }
392 
393 int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *func,
394  const char* mutex_name, ast_mutex_t *t)
395 {
396  int res;
397 
398 #ifdef DEBUG_THREADS
399  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
400  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
401  struct ast_bt *bt = NULL;
402 
403  if (lt) {
404 #ifdef HAVE_BKTR
405  struct ast_bt tmp;
406 
407  /* The implementation of backtrace() may have its own locks.
408  * Capture the backtrace outside of the reentrancy lock to
409  * avoid deadlocks. See ASTERISK-22455. */
410  ast_bt_get_addresses(&tmp);
411 
412  ast_reentrancy_lock(lt);
413  if (lt->reentrancy < AST_MAX_REENTRANCY) {
414  lt->backtrace[lt->reentrancy] = tmp;
415  bt = &lt->backtrace[lt->reentrancy];
416  }
417  ast_reentrancy_unlock(lt);
418 #endif
419  ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
420  }
421 #endif /* DEBUG_THREADS */
422 
423  res = pthread_mutex_trylock(&t->mutex);
424 
425 #ifdef DEBUG_THREADS
426  if (lt && !res) {
427  ast_reentrancy_lock(lt);
428  if (lt->reentrancy < AST_MAX_REENTRANCY) {
429  lt->file[lt->reentrancy] = filename;
430  lt->lineno[lt->reentrancy] = lineno;
431  lt->func[lt->reentrancy] = func;
432  lt->thread_id[lt->reentrancy] = pthread_self();
433  lt->reentrancy++;
434  } else {
435  log_mutex_error(canlog, "%s line %d (%s): '%s' really deep reentrancy!\n",
436  filename, lineno, func, mutex_name);
437  }
438  ast_reentrancy_unlock(lt);
439  ast_mark_lock_acquired(t);
440  } else if (lt) {
441  ast_mark_lock_failed(t);
442  }
443 #endif /* DEBUG_THREADS */
444 
445  return res;
446 }
447 
448 int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *func,
449  const char *mutex_name, ast_mutex_t *t)
450 {
451  int res;
452 
453 #ifdef DEBUG_THREADS
454  struct ast_lock_track *lt = NULL;
455  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
456  struct ast_bt *bt = NULL;
457 
458 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
459  if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
460  log_mutex_error(canlog, "%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
461  filename, lineno, func, mutex_name);
462  DO_THREAD_CRASH;
463  return EINVAL;
464  }
465 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
466 
467  lt = ast_get_reentrancy(&t->track, &t->flags, 0);
468  if (lt) {
469  ast_reentrancy_lock(lt);
470  if (lt->reentrancy && (lt->thread_id[ROFFSET] != pthread_self())) {
471  log_mutex_error(canlog, "%s line %d (%s): attempted unlock mutex '%s' without owning it!\n",
472  filename, lineno, func, mutex_name);
473  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
474  lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
475 #ifdef HAVE_BKTR
476  __dump_backtrace(&lt->backtrace[ROFFSET], canlog);
477 #endif
478  DO_THREAD_CRASH;
479  }
480 
481  if (--lt->reentrancy < 0) {
482  log_mutex_error(canlog, "%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
483  filename, lineno, func, mutex_name);
484  lt->reentrancy = 0;
485  }
486 
487  if (lt->reentrancy < AST_MAX_REENTRANCY) {
488  lt->file[lt->reentrancy] = NULL;
489  lt->lineno[lt->reentrancy] = 0;
490  lt->func[lt->reentrancy] = NULL;
491  lt->thread_id[lt->reentrancy] = 0;
492  }
493 
494 #ifdef HAVE_BKTR
495  if (lt->reentrancy) {
496  bt = &lt->backtrace[lt->reentrancy - 1];
497  }
498 #endif
499  ast_reentrancy_unlock(lt);
500 
501  ast_remove_lock_info(t, bt);
502  }
503 #endif /* DEBUG_THREADS */
504 
505  res = pthread_mutex_unlock(&t->mutex);
506 
507 #ifdef DEBUG_THREADS
508  if (res) {
509  log_mutex_error(canlog, "%s line %d (%s): Error releasing mutex: %s\n",
510  filename, lineno, func, strerror(res));
511  DO_THREAD_CRASH;
512  }
513 #endif /* DEBUG_THREADS */
514 
515  return res;
516 }
517 
518 
519 int __ast_cond_init(const char *filename, int lineno, const char *func,
520  const char *cond_name, ast_cond_t *cond, pthread_condattr_t *cond_attr)
521 {
522  return pthread_cond_init(cond, cond_attr);
523 }
524 
525 int __ast_cond_signal(const char *filename, int lineno, const char *func,
526  const char *cond_name, ast_cond_t *cond)
527 {
528  return pthread_cond_signal(cond);
529 }
530 
531 int __ast_cond_broadcast(const char *filename, int lineno, const char *func,
532  const char *cond_name, ast_cond_t *cond)
533 {
534  return pthread_cond_broadcast(cond);
535 }
536 
537 int __ast_cond_destroy(const char *filename, int lineno, const char *func,
538  const char *cond_name, ast_cond_t *cond)
539 {
540  return pthread_cond_destroy(cond);
541 }
542 
543 #ifdef DEBUG_THREADS
544 static void restore_lock_tracking(struct ast_lock_track *lt, struct ast_lock_track *lt_saved)
545 {
546  ast_reentrancy_lock(lt);
547 
548  /*
549  * The following code must match the struct ast_lock_track
550  * definition with the explicit exception of the reentr_mutex
551  * member.
552  */
553  memcpy(lt->file, lt_saved->file, sizeof(lt->file));
554  memcpy(lt->lineno, lt_saved->lineno, sizeof(lt->lineno));
555  lt->reentrancy = lt_saved->reentrancy;
556  memcpy(lt->func, lt_saved->func, sizeof(lt->func));
557  memcpy(lt->thread_id, lt_saved->thread_id, sizeof(lt->thread_id));
558 #ifdef HAVE_BKTR
559  memcpy(lt->backtrace, lt_saved->backtrace, sizeof(lt->backtrace));
560 #endif
561 
562  ast_reentrancy_unlock(lt);
563 }
564 #endif /* DEBUG_THREADS */
565 
566 int __ast_cond_wait(const char *filename, int lineno, const char *func,
567  const char *cond_name, const char *mutex_name,
568  ast_cond_t *cond, ast_mutex_t *t)
569 {
570  int res;
571 
572 #ifdef DEBUG_THREADS
573  struct ast_lock_track *lt = NULL;
574  struct ast_lock_track lt_orig;
575  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
576 
577 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
578  if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
579  log_mutex_error(canlog, "%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
580  filename, lineno, func, mutex_name);
581  DO_THREAD_CRASH;
582  return EINVAL;
583  }
584 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
585 
586  lt = ast_get_reentrancy(&t->track, &t->flags, 0);
587  if (lt) {
588  ast_reentrancy_lock(lt);
589  if (lt->reentrancy && (lt->thread_id[ROFFSET] != pthread_self())) {
590  log_mutex_error(canlog, "%s line %d (%s): attempted wait using mutex '%s' without owning it!\n",
591  filename, lineno, func, mutex_name);
592  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
593  lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
594 #ifdef HAVE_BKTR
595  __dump_backtrace(&lt->backtrace[ROFFSET], canlog);
596 #endif
597  DO_THREAD_CRASH;
598  } else if (lt->reentrancy <= 0) {
599  log_mutex_error(canlog, "%s line %d (%s): attempted wait using an unlocked mutex '%s'\n",
600  filename, lineno, func, mutex_name);
601  DO_THREAD_CRASH;
602  }
603 
604  /* Waiting on a condition completely suspends a recursive mutex,
605  * even if it's been recursively locked multiple times. Make a
606  * copy of the lock tracking, and reset reentrancy to zero */
607  lt_orig = *lt;
608  lt->reentrancy = 0;
609  ast_reentrancy_unlock(lt);
610 
611  ast_suspend_lock_info(t);
612  }
613 #endif /* DEBUG_THREADS */
614 
615  res = pthread_cond_wait(cond, &t->mutex);
616 
617 #ifdef DEBUG_THREADS
618  if (res) {
619  log_mutex_error(canlog, "%s line %d (%s): Error waiting on condition mutex '%s'\n",
620  filename, lineno, func, strerror(res));
621  DO_THREAD_CRASH;
622  } else if (lt) {
623  restore_lock_tracking(lt, &lt_orig);
624  ast_restore_lock_info(t);
625  }
626 #endif /* DEBUG_THREADS */
627 
628  return res;
629 }
630 
631 int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
632  const char *cond_name, const char *mutex_name, ast_cond_t *cond,
633  ast_mutex_t *t, const struct timespec *abstime)
634 {
635  int res;
636 
637 #ifdef DEBUG_THREADS
638  struct ast_lock_track *lt = NULL;
639  struct ast_lock_track lt_orig;
640  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
641 
642 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
643  if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
644  log_mutex_error(canlog, "%s line %d (%s): Error: mutex '%s' is uninitialized.\n",
645  filename, lineno, func, mutex_name);
646  DO_THREAD_CRASH;
647  return EINVAL;
648  }
649 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
650 
651  lt = ast_get_reentrancy(&t->track, &t->flags, 0);
652  if (lt) {
653  ast_reentrancy_lock(lt);
654  if (lt->reentrancy && (lt->thread_id[ROFFSET] != pthread_self())) {
655  log_mutex_error(canlog, "%s line %d (%s): attempted wait using mutex '%s' without owning it!\n",
656  filename, lineno, func, mutex_name);
657  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
658  lt->file[ROFFSET], lt->lineno[ROFFSET], lt->func[ROFFSET], mutex_name);
659 #ifdef HAVE_BKTR
660  __dump_backtrace(&lt->backtrace[ROFFSET], canlog);
661 #endif
662  DO_THREAD_CRASH;
663  } else if (lt->reentrancy <= 0) {
664  log_mutex_error(canlog, "%s line %d (%s): attempted wait using an unlocked mutex '%s'\n",
665  filename, lineno, func, mutex_name);
666  DO_THREAD_CRASH;
667  }
668 
669  /* Waiting on a condition completely suspends a recursive mutex,
670  * even if it's been recursively locked multiple times. Make a
671  * copy of the lock tracking, and reset reentrancy to zero */
672  lt_orig = *lt;
673  lt->reentrancy = 0;
674  ast_reentrancy_unlock(lt);
675 
676  ast_suspend_lock_info(t);
677  }
678 #endif /* DEBUG_THREADS */
679 
680  res = pthread_cond_timedwait(cond, &t->mutex, abstime);
681 
682 #ifdef DEBUG_THREADS
683  if (res && (res != ETIMEDOUT)) {
684  log_mutex_error(canlog, "%s line %d (%s): Error waiting on condition mutex '%s'\n",
685  filename, lineno, func, strerror(res));
686  DO_THREAD_CRASH;
687  } else if (lt) {
688  restore_lock_tracking(lt, &lt_orig);
689  ast_restore_lock_info(t);
690  }
691 #endif /* DEBUG_THREADS */
692 
693  return res;
694 }
695 
696 int __ast_rwlock_init(int tracking, const char *filename, int lineno, \
697  const char *func, const char *rwlock_name, ast_rwlock_t *t)
698 {
699  int res;
700  pthread_rwlockattr_t attr;
701 
702 #if defined(DEBUG_THREADS) && defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && \
703  defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
704  if (t->lock != ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
705  int canlog = tracking && strcmp(filename, "logger.c");
706 
707  log_mutex_error(canlog, "%s line %d (%s): Warning: rwlock '%s' is already initialized.\n",
708  filename, lineno, func, rwlock_name);
709  DO_THREAD_CRASH;
710  return EBUSY;
711  }
712 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
713 
714 #if defined(DEBUG_THREADS) || defined(DETECT_DEADLOCKS)
715  t->track = NULL;
716  t->flags.tracking = tracking;
717  t->flags.setup = 0;
718 #endif /* DEBUG_THREADS */
719 
720  pthread_rwlockattr_init(&attr);
721 #ifdef HAVE_PTHREAD_RWLOCK_PREFER_WRITER_NP
722  pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NP);
723 #endif
724  res = pthread_rwlock_init(&t->lock, &attr);
725  pthread_rwlockattr_destroy(&attr);
726 
727  return res;
728 }
729 
730 int __ast_rwlock_destroy(const char *filename, int lineno, const char *func, const char *rwlock_name, ast_rwlock_t *t)
731 {
732  int res;
733 
734 #ifdef DEBUG_THREADS
735  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 1);
736  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
737 
738 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
739  if (t->lock == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
740  log_mutex_error(canlog, "%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
741  filename, lineno, func, rwlock_name);
742  DO_THREAD_CRASH;
743  res = EINVAL;
744  goto lt_cleanup;
745  }
746 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
747 #endif /* DEBUG_THREADS */
748 
749  res = pthread_rwlock_destroy(&t->lock);
750 
751 #ifdef DEBUG_THREADS
752  if (res) {
753  log_mutex_error(canlog, "%s line %d (%s): Error destroying rwlock %s: %s\n",
754  filename, lineno, func, rwlock_name, strerror(res));
755  }
756 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
757 lt_cleanup:
758 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
759  if (lt) {
760  ast_reentrancy_lock(lt);
761  lt->file[0] = filename;
762  lt->lineno[0] = lineno;
763  lt->func[0] = func;
764  lt->reentrancy = 0;
765  lt->thread_id[0] = 0;
766 #ifdef HAVE_BKTR
767  memset(&lt->backtrace[0], 0, sizeof(lt->backtrace[0]));
768 #endif
769  ast_reentrancy_unlock(lt);
770  delete_reentrancy_cs(&t->track);
771  }
772 #endif /* DEBUG_THREADS */
773 
774  return res;
775 }
776 
777 int __ast_rwlock_unlock(const char *filename, int line, const char *func, ast_rwlock_t *t, const char *name)
778 {
779  int res;
780 
781 #ifdef DEBUG_THREADS
782  struct ast_lock_track *lt = NULL;
783  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
784  struct ast_bt *bt = NULL;
785  int lock_found = 0;
786 
787 #if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
788  if ((t->lock) == ((pthread_rwlock_t) __AST_RWLOCK_INIT_VALUE)) {
789  log_mutex_error(canlog, "%s line %d (%s): Warning: rwlock '%s' is uninitialized.\n",
790  filename, line, func, name);
791  DO_THREAD_CRASH;
792  return EINVAL;
793  }
794 #endif /* AST_MUTEX_INIT_W_CONSTRUCTORS */
795 
796  lt = ast_get_reentrancy(&t->track, &t->flags, 0);
797  if (lt) {
798  ast_reentrancy_lock(lt);
799  if (lt->reentrancy) {
800  int i;
801  pthread_t self = pthread_self();
802  for (i = lt->reentrancy - 1; i >= 0; --i) {
803  if (lt->thread_id[i] == self) {
804  lock_found = 1;
805  if (i != lt->reentrancy - 1) {
806  lt->file[i] = lt->file[lt->reentrancy - 1];
807  lt->lineno[i] = lt->lineno[lt->reentrancy - 1];
808  lt->func[i] = lt->func[lt->reentrancy - 1];
809  lt->thread_id[i] = lt->thread_id[lt->reentrancy - 1];
810  }
811 #ifdef HAVE_BKTR
812  bt = &lt->backtrace[i];
813 #endif
814  lt->file[lt->reentrancy - 1] = NULL;
815  lt->lineno[lt->reentrancy - 1] = 0;
816  lt->func[lt->reentrancy - 1] = NULL;
817  lt->thread_id[lt->reentrancy - 1] = AST_PTHREADT_NULL;
818  break;
819  }
820  }
821  }
822 
823  if (lock_found && --lt->reentrancy < 0) {
824  log_mutex_error(canlog, "%s line %d (%s): rwlock '%s' freed more times than we've locked!\n",
825  filename, line, func, name);
826  lt->reentrancy = 0;
827  }
828 
829  ast_reentrancy_unlock(lt);
830 
831  ast_remove_lock_info(t, bt);
832  }
833 #endif /* DEBUG_THREADS */
834 
835  res = pthread_rwlock_unlock(&t->lock);
836 
837 #ifdef DEBUG_THREADS
838  if (res) {
839  log_mutex_error(canlog, "%s line %d (%s): Error releasing rwlock: %s\n",
840  filename, line, func, strerror(res));
841  DO_THREAD_CRASH;
842  }
843 #endif /* DEBUG_THREADS */
844 
845  return res;
846 }
847 
848 int __ast_rwlock_rdlock(const char *filename, int line, const char *func,
849  ast_rwlock_t *t, const char *name)
850 {
851  int res;
852 
853 #if defined(DEBUG_THREADS) || defined(DETECT_DEADLOCKS)
854  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
855 #endif
856 
857 #ifdef DEBUG_THREADS
858  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
859  struct ast_bt *bt = NULL;
860 
861  if (lt) {
862 #ifdef HAVE_BKTR
863  struct ast_bt tmp;
864 
865  /* The implementation of backtrace() may have its own locks.
866  * Capture the backtrace outside of the reentrancy lock to
867  * avoid deadlocks. See ASTERISK-22455. */
868  ast_bt_get_addresses(&tmp);
869 
870  ast_reentrancy_lock(lt);
871  if (lt->reentrancy < AST_MAX_REENTRANCY) {
872  lt->backtrace[lt->reentrancy] = tmp;
873  bt = &lt->backtrace[lt->reentrancy];
874  }
875  ast_reentrancy_unlock(lt);
876 #endif
877  ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt);
878  }
879 #endif /* DEBUG_THREADS */
880 
881 #if defined(DETECT_DEADLOCKS)
882  {
883  time_t seconds = time(NULL);
884  time_t wait_time, reported_wait = 0;
885  do {
886  res = pthread_rwlock_tryrdlock(&t->lock);
887  if (res == EBUSY) {
888  wait_time = time(NULL) - seconds;
889  if (wait_time > reported_wait && (wait_time % 5) == 0) {
890  log_mutex_error(canlog, "%s line %d (%s): Deadlock? waited %d sec for readlock '%s'?\n",
891  filename, line, func, (int)wait_time, name);
892 #ifdef DEBUG_THREADS
893  if (lt) {
894  ast_reentrancy_lock(lt);
895 #ifdef HAVE_BKTR
896  __dump_backtrace(&lt->backtrace[lt->reentrancy], canlog);
897 #endif
898  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
899  lt->file[lt->reentrancy-1], lt->lineno[lt->reentrancy-1],
900  lt->func[lt->reentrancy-1], name);
901 #ifdef HAVE_BKTR
902  __dump_backtrace(&lt->backtrace[lt->reentrancy-1], canlog);
903 #endif
904  ast_reentrancy_unlock(lt);
905  }
906 #endif
907  reported_wait = wait_time;
908  }
909  usleep(200);
910  }
911  } while (res == EBUSY);
912  }
913 #else /* !DETECT_DEADLOCKS */
914  res = pthread_rwlock_rdlock(&t->lock);
915 #endif /* !DETECT_DEADLOCKS */
916 
917 #ifdef DEBUG_THREADS
918  if (!res && lt) {
919  ast_reentrancy_lock(lt);
920  if (lt->reentrancy < AST_MAX_REENTRANCY) {
921  lt->file[lt->reentrancy] = filename;
922  lt->lineno[lt->reentrancy] = line;
923  lt->func[lt->reentrancy] = func;
924  lt->thread_id[lt->reentrancy] = pthread_self();
925  lt->reentrancy++;
926  }
927  ast_reentrancy_unlock(lt);
928  ast_mark_lock_acquired(t);
929  } else if (lt) {
930 #ifdef HAVE_BKTR
931  if (lt->reentrancy) {
932  ast_reentrancy_lock(lt);
933  bt = &lt->backtrace[lt->reentrancy-1];
934  ast_reentrancy_unlock(lt);
935  } else {
936  bt = NULL;
937  }
938 #endif
939  ast_remove_lock_info(t, bt);
940  }
941 
942  if (res) {
943  log_mutex_error(canlog, "%s line %d (%s): Error obtaining read lock: %s\n",
944  filename, line, func, strerror(res));
945  DO_THREAD_CRASH;
946  }
947 #endif /* DEBUG_THREADS */
948 
949  return res;
950 }
951 
952 int __ast_rwlock_wrlock(const char *filename, int line, const char *func, \
953  ast_rwlock_t *t, const char *name)
954 {
955  int res;
956 
957 #if defined(DEBUG_THREADS) || defined(DETECT_DEADLOCKS)
958  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
959 #endif
960 
961 #ifdef DEBUG_THREADS
962  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
963  struct ast_bt *bt = NULL;
964 
965  if (lt) {
966 #ifdef HAVE_BKTR
967  struct ast_bt tmp;
968 
969  /* The implementation of backtrace() may have its own locks.
970  * Capture the backtrace outside of the reentrancy lock to
971  * avoid deadlocks. See ASTERISK-22455. */
972  ast_bt_get_addresses(&tmp);
973 
974  ast_reentrancy_lock(lt);
975  if (lt->reentrancy < AST_MAX_REENTRANCY) {
976  lt->backtrace[lt->reentrancy] = tmp;
977  bt = &lt->backtrace[lt->reentrancy];
978  }
979  ast_reentrancy_unlock(lt);
980 #endif
981  ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
982  }
983 #endif /* DEBUG_THREADS */
984 
985 #ifdef DETECT_DEADLOCKS
986  {
987  time_t seconds = time(NULL);
988  time_t wait_time, reported_wait = 0;
989  do {
990  res = pthread_rwlock_trywrlock(&t->lock);
991  if (res == EBUSY) {
992  wait_time = time(NULL) - seconds;
993  if (wait_time > reported_wait && (wait_time % 5) == 0) {
994  log_mutex_error(canlog, "%s line %d (%s): Deadlock? waited %d sec for writelock '%s'?\n",
995  filename, line, func, (int)wait_time, name);
996 #ifdef DEBUG_THREADS
997  if (lt) {
998  ast_reentrancy_lock(lt);
999 #ifdef HAVE_BKTR
1000  __dump_backtrace(&lt->backtrace[lt->reentrancy], canlog);
1001 #endif
1002  log_mutex_error(canlog, "%s line %d (%s): '%s' was locked here.\n",
1003  lt->file[lt->reentrancy-1], lt->lineno[lt->reentrancy-1],
1004  lt->func[lt->reentrancy-1], name);
1005 #ifdef HAVE_BKTR
1006  __dump_backtrace(&lt->backtrace[lt->reentrancy-1], canlog);
1007 #endif
1008  ast_reentrancy_unlock(lt);
1009  }
1010 #endif
1011  reported_wait = wait_time;
1012  }
1013  usleep(200);
1014  }
1015  } while (res == EBUSY);
1016  }
1017 #else /* !DETECT_DEADLOCKS */
1018  res = pthread_rwlock_wrlock(&t->lock);
1019 #endif /* !DETECT_DEADLOCKS */
1020 
1021 #ifdef DEBUG_THREADS
1022  if (!res && lt) {
1023  ast_reentrancy_lock(lt);
1024  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1025  lt->file[lt->reentrancy] = filename;
1026  lt->lineno[lt->reentrancy] = line;
1027  lt->func[lt->reentrancy] = func;
1028  lt->thread_id[lt->reentrancy] = pthread_self();
1029  lt->reentrancy++;
1030  }
1031  ast_reentrancy_unlock(lt);
1032  ast_mark_lock_acquired(t);
1033  } else if (lt) {
1034 #ifdef HAVE_BKTR
1035  if (lt->reentrancy) {
1036  ast_reentrancy_lock(lt);
1037  bt = &lt->backtrace[lt->reentrancy-1];
1038  ast_reentrancy_unlock(lt);
1039  } else {
1040  bt = NULL;
1041  }
1042 #endif
1043  ast_remove_lock_info(t, bt);
1044  }
1045  if (res) {
1046  log_mutex_error(canlog, "%s line %d (%s): Error obtaining write lock: %s\n",
1047  filename, line, func, strerror(res));
1048  DO_THREAD_CRASH;
1049  }
1050 #endif /* DEBUG_THREADS */
1051 
1052  return res;
1053 }
1054 
1055 int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, ast_rwlock_t *t, const char *name,
1056  const struct timespec *abs_timeout)
1057 {
1058  int res;
1059 
1060 #ifdef DEBUG_THREADS
1061  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
1062  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
1063  struct ast_bt *bt = NULL;
1064 
1065  if (lt) {
1066 #ifdef HAVE_BKTR
1067  struct ast_bt tmp;
1068 
1069  /* The implementation of backtrace() may have its own locks.
1070  * Capture the backtrace outside of the reentrancy lock to
1071  * avoid deadlocks. See ASTERISK-22455. */
1072  ast_bt_get_addresses(&tmp);
1073 
1074  ast_reentrancy_lock(lt);
1075  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1076  lt->backtrace[lt->reentrancy] = tmp;
1077  bt = &lt->backtrace[lt->reentrancy];
1078  }
1079  ast_reentrancy_unlock(lt);
1080 #endif
1081  ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
1082  }
1083 #endif /* DEBUG_THREADS */
1084 
1085 #ifdef HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK
1086  res = pthread_rwlock_timedrdlock(&t->lock, abs_timeout);
1087 #else
1088  do {
1089  struct timeval _now;
1090  for (;;) {
1091  if (!(res = pthread_rwlock_tryrdlock(&t->lock))) {
1092  break;
1093  }
1094  _now = ast_tvnow();
1095  if (_now.tv_sec > abs_timeout->tv_sec || (_now.tv_sec == abs_timeout->tv_sec && _now.tv_usec * 1000 > abs_timeout->tv_nsec)) {
1096  break;
1097  }
1098  usleep(1);
1099  }
1100  } while (0);
1101 #endif
1102 
1103 #ifdef DEBUG_THREADS
1104  if (!res && lt) {
1105  ast_reentrancy_lock(lt);
1106  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1107  lt->file[lt->reentrancy] = filename;
1108  lt->lineno[lt->reentrancy] = line;
1109  lt->func[lt->reentrancy] = func;
1110  lt->thread_id[lt->reentrancy] = pthread_self();
1111  lt->reentrancy++;
1112  }
1113  ast_reentrancy_unlock(lt);
1114  ast_mark_lock_acquired(t);
1115  } else if (lt) {
1116 #ifdef HAVE_BKTR
1117  if (lt->reentrancy) {
1118  ast_reentrancy_lock(lt);
1119  bt = &lt->backtrace[lt->reentrancy-1];
1120  ast_reentrancy_unlock(lt);
1121  } else {
1122  bt = NULL;
1123  }
1124 #endif
1125  ast_remove_lock_info(t, bt);
1126  }
1127  if (res) {
1128  log_mutex_error(canlog, "%s line %d (%s): Error obtaining read lock: %s\n",
1129  filename, line, func, strerror(res));
1130  DO_THREAD_CRASH;
1131  }
1132 #endif /* DEBUG_THREADS */
1133 
1134  return res;
1135 }
1136 
1137 int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, ast_rwlock_t *t, const char *name,
1138  const struct timespec *abs_timeout)
1139 {
1140  int res;
1141 
1142 #ifdef DEBUG_THREADS
1143  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
1144  int canlog = t->flags.tracking && strcmp(filename, "logger.c");
1145  struct ast_bt *bt = NULL;
1146 
1147  if (lt) {
1148 #ifdef HAVE_BKTR
1149  struct ast_bt tmp;
1150 
1151  /* The implementation of backtrace() may have its own locks.
1152  * Capture the backtrace outside of the reentrancy lock to
1153  * avoid deadlocks. See ASTERISK-22455. */
1154  ast_bt_get_addresses(&tmp);
1155 
1156  ast_reentrancy_lock(lt);
1157  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1158  lt->backtrace[lt->reentrancy] = tmp;
1159  bt = &lt->backtrace[lt->reentrancy];
1160  }
1161  ast_reentrancy_unlock(lt);
1162 #endif
1163  ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
1164  }
1165 #endif /* DEBUG_THREADS */
1166 
1167 #ifdef HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK
1168  res = pthread_rwlock_timedwrlock(&t->lock, abs_timeout);
1169 #else
1170  do {
1171  struct timeval _now;
1172  for (;;) {
1173  if (!(res = pthread_rwlock_trywrlock(&t->lock))) {
1174  break;
1175  }
1176  _now = ast_tvnow();
1177  if (_now.tv_sec > abs_timeout->tv_sec || (_now.tv_sec == abs_timeout->tv_sec && _now.tv_usec * 1000 > abs_timeout->tv_nsec)) {
1178  break;
1179  }
1180  usleep(1);
1181  }
1182  } while (0);
1183 #endif
1184 
1185 #ifdef DEBUG_THREADS
1186  if (!res && lt) {
1187  ast_reentrancy_lock(lt);
1188  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1189  lt->file[lt->reentrancy] = filename;
1190  lt->lineno[lt->reentrancy] = line;
1191  lt->func[lt->reentrancy] = func;
1192  lt->thread_id[lt->reentrancy] = pthread_self();
1193  lt->reentrancy++;
1194  }
1195  ast_reentrancy_unlock(lt);
1196  ast_mark_lock_acquired(t);
1197  } else if (lt) {
1198 #ifdef HAVE_BKTR
1199  if (lt->reentrancy) {
1200  ast_reentrancy_lock(lt);
1201  bt = &lt->backtrace[lt->reentrancy-1];
1202  ast_reentrancy_unlock(lt);
1203  } else {
1204  bt = NULL;
1205  }
1206 #endif
1207  ast_remove_lock_info(t, bt);
1208  }
1209  if (res) {
1210  log_mutex_error(canlog, "%s line %d (%s): Error obtaining read lock: %s\n",
1211  filename, line, func, strerror(res));
1212  DO_THREAD_CRASH;
1213  }
1214 #endif /* DEBUG_THREADS */
1215 
1216  return res;
1217 }
1218 
1219 int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast_rwlock_t *t, const char *name)
1220 {
1221  int res;
1222 
1223 #ifdef DEBUG_THREADS
1224  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
1225  struct ast_bt *bt = NULL;
1226 
1227  if (lt) {
1228 #ifdef HAVE_BKTR
1229  struct ast_bt tmp;
1230 
1231  /* The implementation of backtrace() may have its own locks.
1232  * Capture the backtrace outside of the reentrancy lock to
1233  * avoid deadlocks. See ASTERISK-22455. */
1234  ast_bt_get_addresses(&tmp);
1235 
1236  ast_reentrancy_lock(lt);
1237  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1238  lt->backtrace[lt->reentrancy] = tmp;
1239  bt = &lt->backtrace[lt->reentrancy];
1240  }
1241  ast_reentrancy_unlock(lt);
1242 #endif
1243  ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt);
1244  }
1245 #endif /* DEBUG_THREADS */
1246 
1247  res = pthread_rwlock_tryrdlock(&t->lock);
1248 
1249 #ifdef DEBUG_THREADS
1250  if (!res && lt) {
1251  ast_reentrancy_lock(lt);
1252  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1253  lt->file[lt->reentrancy] = filename;
1254  lt->lineno[lt->reentrancy] = line;
1255  lt->func[lt->reentrancy] = func;
1256  lt->thread_id[lt->reentrancy] = pthread_self();
1257  lt->reentrancy++;
1258  }
1259  ast_reentrancy_unlock(lt);
1260  ast_mark_lock_acquired(t);
1261  } else if (lt) {
1262  ast_mark_lock_failed(t);
1263  }
1264 #endif /* DEBUG_THREADS */
1265 
1266  return res;
1267 }
1268 
1269 int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast_rwlock_t *t, const char *name)
1270 {
1271  int res;
1272 
1273 #ifdef DEBUG_THREADS
1274  struct ast_lock_track *lt = ast_get_reentrancy(&t->track, &t->flags, 0);
1275  struct ast_bt *bt = NULL;
1276 
1277  if (lt) {
1278 #ifdef HAVE_BKTR
1279  struct ast_bt tmp;
1280 
1281  /* The implementation of backtrace() may have its own locks.
1282  * Capture the backtrace outside of the reentrancy lock to
1283  * avoid deadlocks. See ASTERISK-22455. */
1284  ast_bt_get_addresses(&tmp);
1285 
1286  ast_reentrancy_lock(lt);
1287  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1288  lt->backtrace[lt->reentrancy] = tmp;
1289  bt = &lt->backtrace[lt->reentrancy];
1290  }
1291  ast_reentrancy_unlock(lt);
1292 #endif
1293  ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
1294  }
1295 #endif /* DEBUG_THREADS */
1296 
1297  res = pthread_rwlock_trywrlock(&t->lock);
1298 
1299 #ifdef DEBUG_THREADS
1300  if (!res && lt) {
1301  ast_reentrancy_lock(lt);
1302  if (lt->reentrancy < AST_MAX_REENTRANCY) {
1303  lt->file[lt->reentrancy] = filename;
1304  lt->lineno[lt->reentrancy] = line;
1305  lt->func[lt->reentrancy] = func;
1306  lt->thread_id[lt->reentrancy] = pthread_self();
1307  lt->reentrancy++;
1308  }
1309  ast_reentrancy_unlock(lt);
1310  ast_mark_lock_acquired(t);
1311  } else if (lt) {
1312  ast_mark_lock_failed(t);
1313  }
1314 #endif /* DEBUG_THREADS */
1315 
1316  return res;
1317 }
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Utility functions.
A structure to hold backtrace information. This structure provides an easy means to store backtrace i...
Definition: backtrace.h:50
Lock tracking information.
Definition: lock.h:111
volatile unsigned int setup
Definition: lock.h:127
unsigned int tracking
Definition: lock.h:125
int ast_add_profile(const char *, uint64_t scale)
support for event profiling
Definition: astman.c:92
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
void * addresses[AST_MAX_BT_FRAMES]
Definition: backtrace.h:52
int num_frames
Definition: backtrace.h:54
Structure for rwlock and tracking information.
Definition: lock.h:157
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:253
Structure for mutex and tracking information.
Definition: lock.h:135