Commit 9a7fd6d816 for asterisk.org
commit 9a7fd6d8160ab2fb21853569d016a43c3cf76859
Author: phoneben <3232963@gmail.com>
Date: Thu Oct 15 19:18:21 2026 +0300
main/lock: log failed mutex unlock in non-DEBUG_THREADS builds
main/lock: log failed mutex unlock in non-DEBUG_THREADS builds
In non-DEBUG_THREADS builds, a failed pthread_mutex_unlock() is silent,
so an unlock by a non-owning thread leaves the mutex locked and the only
symptom is a later hang. Log an error naming the mutex, file, line and
function when the unlock fails. The success path costs one branch.
Tested on 20.21.0 with a throwaway module covering both a never-locked
unlock and a cross-thread unlock.
AI assistance: Claude (Anthropic) helped draft this. I reviewed and
tested the change.
Resolves: #2233
diff --git a/main/lock.c b/main/lock.c
index f67fa738eb..b310692e54 100644
--- a/main/lock.c
+++ b/main/lock.c
@@ -509,6 +509,19 @@ int __ast_pthread_mutex_unlock(const char *filename, int lineno, const char *fun
res = pthread_mutex_unlock(&t->mutex);
+#ifndef DEBUG_THREADS
+ /*
+ * An unlock failure (e.g. EPERM) means the mutex may stay locked.
+ * The strcmp only runs when the unlock failed, so there is no
+ * performance impact on normal unlocks. It avoids recursion if the
+ * logger's own mutex fails to unlock.
+ */
+ if (res && strcmp(filename, "logger.c")) {
+ ast_log(LOG_ERROR, "Failed to unlock mutex '%s' at %s:%d (%s): %s\n",
+ mutex_name, filename, lineno, func, strerror(res));
+ }
+#endif
+
#ifdef DEBUG_THREADS
if (res) {
log_mutex_error(canlog, "%s line %d (%s): Error releasing mutex: %s\n",