From c47b3e74d232820b79aa53c61a8fca112d128d20 Mon Sep 17 00:00:00 2001 From: Sean Bright <sean.bright@gmail.com> Date: Wed, 19 Apr 2017 16:08:39 -0400 Subject: [PATCH] pbx: Use same thread if AST_OUTGOING_WAIT_COMPLETE specified Both ast_pbx_outgoing_app() and ast_pbx_outgoing_exten() cause the core to spawn a new thread to perform the dial. When AST_OUTGOING_WAIT_COMPLETE is passed to these functions, the calling thread will be blocked until the newly created channel has been hung up. After this patch, we run the dial on the current thread rather than spawning a new one. The only in-tree code that passes AST_OUTGOING_WAIT_COMPLETE is pbx_spool, so you should see reduced thread usage if you are using .call files. Change-Id: I512735d243f0a9da2bcc128f7a96dece71f2d913 --- main/pbx.c | 76 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/main/pbx.c b/main/pbx.c index dc4b91f084..28027c06b8 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -7529,8 +7529,8 @@ struct pbx_outgoing { int dial_res; /*! \brief Set when dialing is completed */ unsigned int dialed:1; - /*! \brief Set when execution is completed */ - unsigned int executed:1; + /*! \brief Set if we've spawned a thread to do our work */ + unsigned int in_separate_thread:1; }; /*! \brief Destructor for outgoing structure */ @@ -7553,13 +7553,19 @@ static void *pbx_outgoing_exec(void *data) RAII_VAR(struct pbx_outgoing *, outgoing, data, ao2_cleanup); enum ast_dial_result res; - /* Notify anyone interested that dialing is complete */ res = ast_dial_run(outgoing->dial, NULL, 0); - ao2_lock(outgoing); - outgoing->dial_res = res; - outgoing->dialed = 1; - ast_cond_signal(&outgoing->cond); - ao2_unlock(outgoing); + + if (outgoing->in_separate_thread) { + /* Notify anyone interested that dialing is complete */ + ao2_lock(outgoing); + outgoing->dial_res = res; + outgoing->dialed = 1; + ast_cond_signal(&outgoing->cond); + ao2_unlock(outgoing); + } else { + /* We still need the dial result, but we don't need to lock */ + outgoing->dial_res = res; + } /* If the outgoing leg was not answered we can immediately return and go no further */ if (res != AST_DIAL_RESULT_ANSWERED) { @@ -7599,12 +7605,6 @@ static void *pbx_outgoing_exec(void *data) } } - /* Notify anyone else again that may be interested that execution is complete */ - ao2_lock(outgoing); - outgoing->executed = 1; - ast_cond_signal(&outgoing->cond); - ao2_unlock(outgoing); - return NULL; } @@ -7810,34 +7810,42 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, } } + /* This extra reference is dereferenced by pbx_outgoing_exec */ ao2_ref(outgoing, +1); - if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) { - ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr); - ao2_ref(outgoing, -1); - if (locked_channel) { - if (!synchronous) { - ast_channel_unlock(dialed); + + if (synchronous == AST_OUTGOING_WAIT_COMPLETE) { + /* + * Because we are waiting until this is complete anyway, there is no + * sense in creating another thread that we will just need to wait + * for, so instead we commandeer the current thread. + */ + pbx_outgoing_exec(outgoing); + } else { + outgoing->in_separate_thread = 1; + + if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) { + ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr); + ao2_ref(outgoing, -1); + if (locked_channel) { + if (!synchronous) { + ast_channel_unlock(dialed); + } + ast_channel_unref(dialed); } - ast_channel_unref(dialed); + return -1; } - return -1; - } - if (synchronous) { - ao2_lock(outgoing); - /* Wait for dialing to complete */ - while (!outgoing->dialed) { - ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing)); - } - if (1 < synchronous - && outgoing->dial_res == AST_DIAL_RESULT_ANSWERED) { - /* Wait for execution to complete */ - while (!outgoing->executed) { + if (synchronous) { + ao2_lock(outgoing); + /* Wait for dialing to complete */ + while (!outgoing->dialed) { ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing)); } + ao2_unlock(outgoing); } - ao2_unlock(outgoing); + } + if (synchronous) { /* Determine the outcome of the dialing attempt up to it being answered. */ if (reason) { *reason = pbx_dial_reason(outgoing->dial_res,