From 6b3367bf5c22dda34d59dd2174f9555d3ad60a54 Mon Sep 17 00:00:00 2001
From: Olle Johansson <oej@edvina.net>
Date: Sat, 15 Apr 2006 08:07:50 +0000
Subject: [PATCH] New functions for locking a channel - these simplify
 debugging when you have channel locking issues. (Part of the SIP transfer
 patch, where I had a *lot* of channel locking problems)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@20264 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 Makefile                |   4 ++
 channel.c               | 124 ++++++++++++++++++++++++++++++++++++++++
 channels/chan_sip.c     |   4 +-
 include/asterisk/lock.h |  12 ++++
 4 files changed, 142 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index a28541393c..ff9d077e32 100644
--- a/Makefile
+++ b/Makefile
@@ -90,6 +90,10 @@ WITH_SMDI = 1
 # Optional debugging parameters
 DEBUG_THREADS = #-DDUMP_SCHEDULER #-DDEBUG_SCHEDULER #-DDEBUG_THREADS #-DDO_CRASH #-DDETECT_DEADLOCKS
 
+# If you want to debug channel locking, try this (depends on code using
+# ast_channel_lock and companions to work)
+DEBUG_THREADS += #-DDEBUG_CHANNEL_LOCKS
+
 # Uncomment next one to enable ast_frame tracing (for debugging)
 TRACE_FRAMES = #-DTRACE_FRAMES
 
diff --git a/channel.c b/channel.c
index f813e32f88..303cc32c19 100644
--- a/channel.c
+++ b/channel.c
@@ -4187,3 +4187,127 @@ const char *channelreloadreason2txt(enum channelreloadreason reason)
 		return "MANAGERRELOAD (Channel module reload by manager)";
 	}
 };
+
+#ifdef DEBUG_CHANNEL_LOCKS
+
+/*! \brief Unlock AST channel (and print debugging output) 
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan)
+{
+	int res = 0;
+	if (option_debug > 2) 
+		ast_log(LOG_DEBUG, "::::==== Unlocking AST channel %s\n", chan->name);
+	
+	if (!chan) {
+		ast_log(LOG_DEBUG, "::::==== Unlocking non-existing channel \n");
+		return 0;
+	}
+
+	res = ast_mutex_unlock(&chan->lock);
+
+	if (option_debug > 2) {
+		/* Try to find counter if possible on your platform 
+			I've only found out how to do this on Linux
+			DEBUG_THREADS changes the lock structure
+		*/
+#ifdef __linux__
+		int count = 0;
+#ifdef DEBUG_THREADS
+		if ((count = chan->lock.mutex.__m_count))
+#else
+		if ((count = chan->lock.__m_count))
+#endif
+			ast_log(LOG_DEBUG, ":::=== Still have %d locks (recursive)\n", count);
+#endif
+		if (!res)
+			ast_log(LOG_DEBUG, "::::==== Channel %s was unlocked\n", chan->name);
+			if (res == EINVAL) {
+				ast_log(LOG_DEBUG, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
+			}
+		}
+		if (res == EPERM) {
+			/* We had no lock, so okay any way*/
+			if (option_debug > 3)
+				ast_log(LOG_DEBUG, "::::==== Channel %s was not locked at all \n", chan->name);
+		res = 0;
+	}
+	return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan)
+{
+	int res;
+
+	if (option_debug > 3)
+		ast_log(LOG_DEBUG, "====:::: Locking AST channel %s\n", chan->name);
+
+	res = ast_mutex_lock(&chan->lock);
+
+	if (option_debug > 3) {
+#ifdef __linux__
+		int count = 0;
+#ifdef DEBUG_THREADS
+		if ((count = chan->lock.mutex.__m_count))
+#else
+		if ((count = chan->lock.__m_count))
+#endif
+			ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+		if (!res)
+			ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
+		if (res == EDEADLK) {
+		/* We had no lock, so okey any way */
+		if (option_debug > 3)
+			ast_log(LOG_DEBUG, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
+		}
+		if (res == EINVAL) {
+			if (option_debug > 3)
+				ast_log(LOG_DEBUG, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+		}
+	}
+	return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note	You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int __ast_channel_trylock(struct ast_channel *chan)
+{
+	int res;
+
+	if (option_debug > 2)
+		ast_log(LOG_DEBUG, "====:::: Trying to lock AST channel %s\n", chan->name);
+
+	res = ast_mutex_trylock(&chan->lock);
+
+	if (option_debug > 2) {
+#ifdef __linux__
+		int count = 0;
+#ifdef DEBUG_THREADS
+		if ((count = chan->lock.mutex.__m_count))
+#else
+		if ((count = chan->lock.__m_count))
+#endif
+			ast_log(LOG_DEBUG, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+		if (!res)
+			ast_log(LOG_DEBUG, "::::==== Channel %s was locked\n", chan->name);
+		if (res == EBUSY) {
+			/* We failed to lock */
+			if (option_debug > 2)
+				ast_log(LOG_DEBUG, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
+		}
+		if (res == EDEADLK) {
+			/* We had no lock, so okey any way*/
+			if (option_debug > 2)
+				ast_log(LOG_DEBUG, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
+		}
+		if (res == EINVAL && option_debug > 2)
+			ast_log(LOG_DEBUG, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+	}
+	return res;
+}
+
+#endif
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 2f123918d5..262496945a 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1344,7 +1344,7 @@ static int retrans_pkt(void *data)
 	int reschedule = DEFAULT_RETRANS;
 
 	/* Lock channel */
-	ast_mutex_lock(&pkt->owner->lock);
+	ast_channel_lock(&pkt->owner);
 
 	if (pkt->retrans < MAX_RETRANS) {
 		pkt->retrans++;
@@ -1381,7 +1381,7 @@ static int retrans_pkt(void *data)
 
 		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
 		__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
-		ast_mutex_unlock(&pkt->owner->lock);
+		ast_channel_unlock(&pkt->owner);
 		return  reschedule;
 	} 
 	/* Too many retries */
diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h
index 97deac143f..ab6dde3fc4 100644
--- a/include/asterisk/lock.h
+++ b/include/asterisk/lock.h
@@ -761,4 +761,16 @@ AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
 }
 )
 
+#ifndef DEBUG_CHANNEL_LOCKS
+/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined 
+	in the Makefile, print relevant output for debugging */
+#define ast_channel_lock(x)		ast_mutex_lock(x->lock);
+/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined 
+	in the Makefile, print relevant output for debugging */
+#define ast_channel_unlock(x)		ast_mutex_unlock(x->lock);
+/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined 
+	in the Makefile, print relevant output for debugging */
+#define ast_channel_trylock(x)		ast_mutex_trylock(x->lock);
+#endif
+
 #endif /* _ASTERISK_LOCK_H */
-- 
GitLab