summaryrefslogtreecommitdiff
path: root/main/lock.c
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-09-09 20:13:40 +0000
committerDavid M. Lee <dlee@digium.com>2013-09-09 20:13:40 +0000
commitc2e6e1ef49d8e8745c9e4b6be2601b210a34506f (patch)
tree000084953c842ca083e688bcf8f36eed0cfd9747 /main/lock.c
parent0bcc676d09bbf11e02368ababe280795de61746d (diff)
Fix DEBUG_THREADS when lock is acquired in __constructor__
This patch fixes some long-standing bugs in debug threads that were exacerbated with recent Optional API work in Asterisk 12. With debug threads enabled, on some systems, there's a lock ordering problem between our mutex and glibc's mutex protecting its module list (Ubuntu Lucid, glibc 2.11.1 in this instance). In one thread, the module list will be locked before acquiring our mutex. In another thread, our mutex will be locked before locking the module list (which happens in the depths of calling backtrace()). This patch fixes this issue by moving backtrace() calls outside of critical sections that have the mutex acquired. The bigger change was to reentrancy tracking for ast_cond_{timed,}wait, which wrongly assumed that waiting on the mutex was equivalent to a single unlock (it actually suspends all recursive locks on the mutex). (closes issue ASTERISK-22455) Review: https://reviewboard.asterisk.org/r/2824/ ........ Merged revisions 398648 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 398649 from http://svn.asterisk.org/svn/asterisk/branches/11 ........ Merged revisions 398651 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@398652 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/lock.c')
-rw-r--r--main/lock.c203
1 files changed, 107 insertions, 96 deletions
diff --git a/main/lock.c b/main/lock.c
index 3036e5bc4..d66e01b27 100644
--- a/main/lock.c
+++ b/main/lock.c
@@ -208,12 +208,20 @@ int __ast_pthread_mutex_lock(const char *filename, int lineno, const char *func,
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
#else
ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t);
@@ -340,12 +348,20 @@ int __ast_pthread_mutex_trylock(const char *filename, int lineno, const char *fu
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
#else
ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t);
@@ -497,10 +513,8 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
#ifdef DEBUG_THREADS
struct ast_lock_track *lt;
+ struct ast_lock_track lt_orig;
int canlog = strcmp(filename, "logger.c") & t->tracking;
-#ifdef HAVE_BKTR
- struct ast_bt *bt = NULL;
-#endif
#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
@@ -531,33 +545,20 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
__dump_backtrace(&lt->backtrace[ROFFSET], canlog);
#endif
DO_THREAD_CRASH;
+ } else if (lt->reentrancy <= 0) {
+ __ast_mutex_logger("%s line %d (%s): attempted to wait on an unlocked mutex '%s'\n",
+ filename, lineno, func, mutex_name);
+ DO_THREAD_CRASH;
}
- if (--lt->reentrancy < 0) {
- __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
- filename, lineno, func, mutex_name);
- lt->reentrancy = 0;
- }
-
- if (lt->reentrancy < AST_MAX_REENTRANCY) {
- lt->file[lt->reentrancy] = NULL;
- lt->lineno[lt->reentrancy] = 0;
- lt->func[lt->reentrancy] = NULL;
- lt->thread[lt->reentrancy] = 0;
- }
-
-#ifdef HAVE_BKTR
- if (lt->reentrancy) {
- bt = &lt->backtrace[lt->reentrancy - 1];
- }
-#endif
+ /* Waiting on a condition completely suspends a recursive mutex,
+ * even if it's been recursively locked multiple times. Make a
+ * copy of the lock tracking, and reset reentrancy to zero */
+ lt_orig = *lt;
+ lt->reentrancy = 0;
ast_reentrancy_unlock(lt);
-#ifdef HAVE_BKTR
- ast_remove_lock_info(t, bt);
-#else
- ast_remove_lock_info(t);
-#endif
+ ast_suspend_lock_info(t);
}
#endif /* DEBUG_THREADS */
@@ -569,28 +570,16 @@ int __ast_cond_wait(const char *filename, int lineno, const char *func,
filename, lineno, func, strerror(res));
DO_THREAD_CRASH;
} else if (t->tracking) {
+ pthread_mutex_t reentr_mutex_orig;
ast_reentrancy_lock(lt);
- if (lt->reentrancy < AST_MAX_REENTRANCY) {
- lt->file[lt->reentrancy] = filename;
- lt->lineno[lt->reentrancy] = lineno;
- lt->func[lt->reentrancy] = func;
- lt->thread[lt->reentrancy] = pthread_self();
-#ifdef HAVE_BKTR
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
- bt = &lt->backtrace[lt->reentrancy];
-#endif
- lt->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
- }
+ /* Restore lock tracking to what it was prior to the wait */
+ reentr_mutex_orig = lt->reentr_mutex;
+ *lt = lt_orig;
+ /* un-trash the mutex we just copied over */
+ lt->reentr_mutex = reentr_mutex_orig;
ast_reentrancy_unlock(lt);
-#ifdef HAVE_BKTR
- ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
-#else
- ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t);
-#endif
+ ast_restore_lock_info(t);
}
#endif /* DEBUG_THREADS */
@@ -605,10 +594,8 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
#ifdef DEBUG_THREADS
struct ast_lock_track *lt;
+ struct ast_lock_track lt_orig;
int canlog = strcmp(filename, "logger.c") & t->tracking;
-#ifdef HAVE_BKTR
- struct ast_bt *bt = NULL;
-#endif
#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS) && defined(CAN_COMPARE_MUTEX_TO_INIT_VALUE)
if ((t->mutex) == ((pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER)) {
@@ -639,32 +626,20 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
__dump_backtrace(&lt->backtrace[ROFFSET], canlog);
#endif
DO_THREAD_CRASH;
- }
-
- if (--lt->reentrancy < 0) {
- __ast_mutex_logger("%s line %d (%s): mutex '%s' freed more times than we've locked!\n",
+ } else if (lt->reentrancy <= 0) {
+ __ast_mutex_logger("%s line %d (%s): attempted to wait on an unlocked mutex '%s'\n",
filename, lineno, func, mutex_name);
- lt->reentrancy = 0;
+ DO_THREAD_CRASH;
}
- if (lt->reentrancy < AST_MAX_REENTRANCY) {
- lt->file[lt->reentrancy] = NULL;
- lt->lineno[lt->reentrancy] = 0;
- lt->func[lt->reentrancy] = NULL;
- lt->thread[lt->reentrancy] = 0;
- }
-#ifdef HAVE_BKTR
- if (lt->reentrancy) {
- bt = &lt->backtrace[lt->reentrancy - 1];
- }
-#endif
+ /* Waiting on a condition completely suspends a recursive mutex,
+ * even if it's been recursively locked multiple times. Make a
+ * copy of the lock tracking, and reset reentrancy to zero */
+ lt_orig = *lt;
+ lt->reentrancy = 0;
ast_reentrancy_unlock(lt);
-#ifdef HAVE_BKTR
- ast_remove_lock_info(t, bt);
-#else
- ast_remove_lock_info(t);
-#endif
+ ast_suspend_lock_info(t);
}
#endif /* DEBUG_THREADS */
@@ -676,28 +651,16 @@ int __ast_cond_timedwait(const char *filename, int lineno, const char *func,
filename, lineno, func, strerror(res));
DO_THREAD_CRASH;
} else if (t->tracking) {
+ pthread_mutex_t reentr_mutex_orig;
ast_reentrancy_lock(lt);
- if (lt->reentrancy < AST_MAX_REENTRANCY) {
- lt->file[lt->reentrancy] = filename;
- lt->lineno[lt->reentrancy] = lineno;
- lt->func[lt->reentrancy] = func;
- lt->thread[lt->reentrancy] = pthread_self();
-#ifdef HAVE_BKTR
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
- bt = &lt->backtrace[lt->reentrancy];
-#endif
- lt->reentrancy++;
- } else {
- __ast_mutex_logger("%s line %d (%s): '%s' really deep reentrancy!\n",
- filename, lineno, func, mutex_name);
- }
+ /* Restore lock tracking to what it was prior to the wait */
+ reentr_mutex_orig = lt->reentr_mutex;
+ *lt = lt_orig;
+ /* un-trash the mutex we just copied over */
+ lt->reentr_mutex = reentr_mutex_orig;
ast_reentrancy_unlock(lt);
-#ifdef HAVE_BKTR
- ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t, bt);
-#else
- ast_store_lock_info(AST_MUTEX, filename, lineno, func, mutex_name, t);
-#endif
+ ast_suspend_lock_info(t);
}
#endif /* DEBUG_THREADS */
@@ -899,12 +862,20 @@ int __ast_rwlock_rdlock(const char *filename, int line, const char *func, ast_rw
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t);
@@ -1018,12 +989,20 @@ int __ast_rwlock_wrlock(const char *filename, int line, const char *func, ast_rw
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t);
@@ -1141,12 +1120,20 @@ int __ast_rwlock_timedrdlock(const char *filename, int line, const char *func, a
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t);
@@ -1244,12 +1231,20 @@ int __ast_rwlock_timedwrlock(const char *filename, int line, const char *func, a
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t);
@@ -1350,12 +1345,20 @@ int __ast_rwlock_tryrdlock(const char *filename, int line, const char *func, ast
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_RDLOCK, filename, line, func, name, t);
@@ -1420,12 +1423,20 @@ int __ast_rwlock_trywrlock(const char *filename, int line, const char *func, ast
if (t->tracking) {
#ifdef HAVE_BKTR
+ struct ast_bt tmp;
+
+ /* The implementation of backtrace() may have its own locks.
+ * Capture the backtrace outside of the reentrancy lock to
+ * avoid deadlocks. See ASTERISK-22455. */
+ ast_bt_get_addresses(&tmp);
+
ast_reentrancy_lock(lt);
if (lt->reentrancy != AST_MAX_REENTRANCY) {
- ast_bt_get_addresses(&lt->backtrace[lt->reentrancy]);
+ lt->backtrace[lt->reentrancy] = tmp;
bt = &lt->backtrace[lt->reentrancy];
}
ast_reentrancy_unlock(lt);
+
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t, bt);
#else
ast_store_lock_info(AST_WRLOCK, filename, line, func, name, t);