Index: main/pbx.c =================================================================== --- main/pbx.c (revision 386603) +++ main/pbx.c (working copy) @@ -1212,7 +1212,7 @@ * This lock MUST be recursive, or a deadlock on reload may result. See * https://issues.asterisk.org/view.php?id=17643 */ -AST_MUTEX_DEFINE_STATIC(conlock); +AST_RWLOCK_DEFINE_STATIC(conlock); /*! * \brief Lock to hold off restructuring of hints by ast_merge_contexts_and_delete. @@ -10614,19 +10614,75 @@ /* * Lock context list functions ... */ +AST_THREADSTORAGE(conlock_read_count); +struct conlock_count { + unsigned int count; + unsigned int isreadwrite:2; +}; +#define CONLOCK_READ 1 +#define CONLOCK_WRITE 2 + int ast_wrlock_contexts(void) { - return ast_mutex_lock(&conlock); + struct conlock_count *buf = ast_threadstorage_get(&conlock_read_count, sizeof(*buf)); + int res = 0; + /* An original wrlock may be escalated by either rdlock or wrlock, but an + * original rdlock is escalatable only by another rdlock. */ + if (buf->isreadwrite == CONLOCK_READ) { + return EDEADLK; + } + + if (buf->count > 0) { + buf->count++; + } else if ((res = ast_rwlock_wrlock(&conlock)) == 0) { + buf->count = 1; + buf->isreadwrite = CONLOCK_WRITE; + } else { + ast_log(LOG_ERROR, "Unhandled lock error condition?!! (%d) %s\n", res, strerror(res)); + } + return res; } int ast_rdlock_contexts(void) { - return ast_mutex_lock(&conlock); + struct conlock_count *buf = ast_threadstorage_get(&conlock_read_count, sizeof(*buf)); + int res = 0; + + if (buf->count > 0) { + /* Already locked -- make it recursively locked one more time. */ + buf->count++; + } else if ((res = ast_rwlock_rdlock(&conlock)) == 0) { + buf->isreadwrite = CONLOCK_READ; + buf->count = 1; + } else { + ast_log(LOG_ERROR, "Unhandled lock error condition?!! (%d) %s\n", res, strerror(res)); + } + return res; } int ast_unlock_contexts(void) { - return ast_mutex_unlock(&conlock); + struct conlock_count *buf = ast_threadstorage_get(&conlock_read_count, sizeof(*buf)); + int res = 0; + if (buf->count > 1) { + buf->count--; + } else if (buf->count == 0) { + /* No lock held */ + return EPERM; + } else if ((res = ast_rwlock_unlock(&conlock)) == 0) { + buf->count = 0; + /* Reset flags, so next time in the same thread, we can change the type of lock. */ + buf->isreadwrite = 0; + } else if (res == EPERM) { + /* This should never occur -- it would mean that we unlock conlock outside of this function. */ + ast_log(LOG_ERROR, "Programming bug -- unlocked elsewhere?!!\n"); + buf->count = 0; + buf->isreadwrite = 0; + } else { + ast_log(LOG_ERROR, "Unhandled lock error: (%d) %s\n", res, strerror(res)); + } + + return res; } /*