diff --git a/Makefile b/Makefile index a28541393c86aa838c55f9da0c601f78f37b53c1..ff9d077e32a4c8ef5f03e30882676a8888426a03 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 f813e32f88b01ae54b162ba908be1d2507859266..303cc32c19983c96d1de9ab6a658cd00382267be 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 2f123918d56900aae7d8c5adaf4d33dd5c2f928b..262496945a9e1c69e432517cd05c734331121d9b 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 97deac143fdd0994654b9eb75fecd06018cc711c..ab6dde3fc488a0427e2859b561ebe06b0f3fdffa 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 */