diff options
author | Alexei Gradinari <alex2grad@gmail.com> | 2016-08-06 11:57:08 -0400 |
---|---|---|
committer | Alexei Gradinari <alex2grad@gmail.com> | 2016-08-10 15:14:38 -0500 |
commit | 820879415f7b6dd7ea300b6b43308d8cffb7256d (patch) | |
tree | 6c3e41082f474332d571fe29d95a87b982798592 | |
parent | d78fe8fed0d0a76e11b2884a65b66e82b28a6019 (diff) |
pjsip: Fix deadlock with suspend taskprocessor on masquerade
If both channels which should be masqueraded
are in the same serializer:
1st channel will be locked waiting condition 'complete'
2nd channel will be locked waiting condition 'suspended'
On heavy load system a chance that both channels will be in
the same serializer 'pjsip/distibutor' is very high.
To reproduce compile res_pjsip/pjsip_distributor.c with
DISTRIBUTOR_POOL_SIZE=1
Steps to reproduce:
1. Party A calls Party B (bridged call 'AB')
2. Party B places Party A on hold
3. Party B calls Voicemail app (non-bridged call 'BV')
4. Party B attended transfers Party A to voicemail using REFER.
5. When asterisk masquerades calls 'AB' and 'BV',
a deadlock is happened.
This patch adds a suspension indicator to the taskprocessor.
When a session suspends/unsuspends the serializer
it sets the indicator to the appropriate state.
The session checks the suspension indicator before
suspend the serializer.
ASTERISK-26145 #close
Change-Id: Iaaebee60013a58c942ba47b1b4930a63e686663b
-rw-r--r-- | include/asterisk/taskprocessor.h | 32 | ||||
-rw-r--r-- | main/taskprocessor.c | 29 | ||||
-rw-r--r-- | res/res_pjsip_session.c | 9 |
3 files changed, 70 insertions, 0 deletions
diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h index e51122269..7c79036b3 100644 --- a/include/asterisk/taskprocessor.h +++ b/include/asterisk/taskprocessor.h @@ -242,6 +242,38 @@ int ast_taskprocessor_push_local(struct ast_taskprocessor *tps, int (*task_exe)(struct ast_taskprocessor_local *local), void *datap); /*! + * \brief Indicate the taskprocessor is suspended. + * + * \since 13.12.0 + * + * \param tps Task processor. + * \retval 0 success + * \retval -1 failure + */ +int ast_taskprocessor_suspend(struct ast_taskprocessor *tps); + +/*! + * \brief Indicate the taskprocessor is unsuspended. + * + * \since 13.12.0 + * + * \param tps Task processor. + * \retval 0 success + * \retval -1 failure + */ +int ast_taskprocessor_unsuspend(struct ast_taskprocessor *tps); + +/*! + * \brief Get the task processor suspend status + * + * \since 13.12.0 + * + * \param tps Task processor. + * \retval non-zero if the task processor is suspended + */ +int ast_taskprocessor_is_suspended(struct ast_taskprocessor *tps); + +/*! * \brief Pop a task off the taskprocessor and execute it. * * \since 12.0.0 diff --git a/main/taskprocessor.c b/main/taskprocessor.c index 2f0124045..4a8497f11 100644 --- a/main/taskprocessor.c +++ b/main/taskprocessor.c @@ -91,6 +91,8 @@ struct ast_taskprocessor { unsigned int high_water_warned:1; /*! Indicates that a high water alert is active on this taskprocessor */ unsigned int high_water_alert:1; + /*! Indicates if the taskprocessor is currently suspended */ + unsigned int suspended:1; }; /*! @@ -910,6 +912,33 @@ int ast_taskprocessor_push_local(struct ast_taskprocessor *tps, int (*task_exe)( return taskprocessor_push(tps, tps_task_alloc_local(task_exe, datap)); } +int ast_taskprocessor_suspend(struct ast_taskprocessor *tps) +{ + if (tps) { + ao2_lock(tps); + tps->suspended = 1; + ao2_unlock(tps); + return 0; + } + return -1; +} + +int ast_taskprocessor_unsuspend(struct ast_taskprocessor *tps) +{ + if (tps) { + ao2_lock(tps); + tps->suspended = 0; + ao2_unlock(tps); + return 0; + } + return -1; +} + +int ast_taskprocessor_is_suspended(struct ast_taskprocessor *tps) +{ + return tps ? tps->suspended : -1; +} + int ast_taskprocessor_execute(struct ast_taskprocessor *tps) { struct ast_taskprocessor_local local; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index a773c16fc..488492f4f 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1559,6 +1559,11 @@ void ast_sip_session_suspend(struct ast_sip_session *session) return; } + if (ast_taskprocessor_is_suspended(session->serializer)) { + /* The serializer already suspended. */ + return; + } + suspender = ao2_alloc(sizeof(*suspender), sip_session_suspender_dtor); if (!suspender) { /* We will just have to hope that the system does not deadlock */ @@ -1583,6 +1588,8 @@ void ast_sip_session_suspend(struct ast_sip_session *session) ast_cond_wait(&suspender->cond_suspended, ao2_object_get_lockaddr(suspender)); } ao2_unlock(suspender); + + ast_taskprocessor_suspend(session->serializer); } void ast_sip_session_unsuspend(struct ast_sip_session *session) @@ -1602,6 +1609,8 @@ void ast_sip_session_unsuspend(struct ast_sip_session *session) ao2_unlock(suspender); ao2_ref(suspender, -1); + + ast_taskprocessor_unsuspend(session->serializer); } /*! |