<p>Joshua C. Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/11001">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Richard Mudgett: Looks good to me, but someone else must approve
Kevin Harwell: Looks good to me, but someone else must approve
Joshua C. Colp: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">taskprocessor: Enable subsystems and overload by subsystem<br><br>To prevent one subsystem's taskprocessors from causing others<br>to stall, new capabilities have been added to taskprocessors.<br><br>* Any taskprocessor name that has a '/' will have the part<br> before the '/' saved as its "subsystem".<br> Examples:<br> "sorcery/acl-0000006a" and "sorcery/aor-00000019"<br> will be grouped to subsystem "sorcery".<br> "pjsip/distributor-00000025" and "pjsip/distributor-00000026"<br> will bn grouped to subsystem "pjsip".<br> Taskprocessors with no '/' have an empty subsystem.<br><br>* When a taskprocessor enters high-water alert status and it<br> has a non-empty subsystem, the subsystem alert count will<br> be incremented.<br><br>* When a taskprocessor leaves high-water alert status and it<br> has a non-empty subsystem, the subsystem alert count will be<br> decremented.<br><br>* A new api ast_taskprocessor_get_subsystem_alert() has been<br> added that returns the number of taskprocessors in alert for<br> the subsystem.<br><br>* A new CLI command "core show taskprocessor alerted subsystems"<br> has been added.<br><br>* A new unit test was addded.<br><br>REMINDER: The taskprocessor code itself doesn't take any action<br>based on high-water alerts or overloading. It's up to taskprocessor<br>users to check and take action themselves. Currently only the pjsip<br>distributor does this.<br><br>* A new pjsip/global option "taskprocessor_overload_trigger"<br> has been added that allows the user to select the trigger<br> mechanism the distributor uses to pause accepting new requests.<br> "none": Don't pause on any overload condition.<br> "global": Pause on ANY taskprocessor overload (the default and<br> current behavior)<br> "pjsip_only": Pause only on pjsip taskprocessor overloads.<br><br>* The core pjsip pool was renamed from "SIP" to "pjsip" so it can<br> be properly grouped into the "pjsip" subsystem.<br><br>* stasis taskprocessor names were changed to "stasis" as the<br> subsystem.<br><br>* Sorcery core taskprocessor names were changed to "sorcery" to<br> match the object taskprocessors.<br><br>Change-Id: I8c19068bb2fc26610a9f0b8624bdf577a04fcd56<br>---<br>M CHANGES<br>M configs/samples/pjsip.conf.sample<br>A contrib/ast-db-manage/config/versions/f3c0b8695b66_taskprocessor_overload_trigger.py<br>M include/asterisk/taskprocessor.h<br>M main/sorcery.c<br>M main/stasis.c<br>M main/taskprocessor.c<br>M main/threadpool.c<br>M res/res_pjsip.c<br>M res/res_pjsip/config_global.c<br>M res/res_pjsip/include/res_pjsip_private.h<br>M res/res_pjsip/pjsip_distributor.c<br>M tests/test_taskprocessor.c<br>13 files changed, 525 insertions(+), 12 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/CHANGES b/CHANGES</span><br><span>index 6083b1a..5b418ce 100644</span><br><span>--- a/CHANGES</span><br><span>+++ b/CHANGES</span><br><span>@@ -20,6 +20,15 @@</span><br><span> types defined in the "disallowed" list are not sent to the application. Note</span><br><span> that if a type is specified in both lists "disallowed" takes precedence.</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+res_pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+------------------</span><br><span style="color: hsl(120, 100%, 40%);">+ * A new configuration parameter "taskprocessor_overload_trigger" has been</span><br><span style="color: hsl(120, 100%, 40%);">+ added to the pjsip.conf "globals" section. The distributor currently stops</span><br><span style="color: hsl(120, 100%, 40%);">+ accepting new requests when any taskprocessor overload is triggered. The</span><br><span style="color: hsl(120, 100%, 40%);">+ new option allows you to completely disable overload detection (NOT</span><br><span style="color: hsl(120, 100%, 40%);">+ RECOMMENDED), keep the current behavior, or trigger only on pjsip</span><br><span style="color: hsl(120, 100%, 40%);">+ taskprocessor overloads.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ------------------------------------------------------------------------------</span><br><span> --- Functionality changes from Asterisk 13.24.0 to Asterisk 13.25.0 ----------</span><br><span> ------------------------------------------------------------------------------</span><br><span>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample</span><br><span>index a04ce05..9483af5 100644</span><br><span>--- a/configs/samples/pjsip.conf.sample</span><br><span>+++ b/configs/samples/pjsip.conf.sample</span><br><span>@@ -1111,6 +1111,17 @@</span><br><span> ; event when a device refreshes its registration</span><br><span> ; (default: "yes")</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+;taskprocessor_overload_trigger=global</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Set the trigger the distributor will use to detect</span><br><span style="color: hsl(120, 100%, 40%);">+ ; taskprocessor overloads. When triggered, the distributor</span><br><span style="color: hsl(120, 100%, 40%);">+ ; will not accept any new requests until the overload has</span><br><span style="color: hsl(120, 100%, 40%);">+ ; cleared.</span><br><span style="color: hsl(120, 100%, 40%);">+ : "global": (default) Any taskprocessor overload will trigger.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; "pjsip_only": Only pjsip taskprocessor overloads will trigger.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; "none": No overload detection will be performed.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; WARNING: The "none" and "pjsip_only" options should be used</span><br><span style="color: hsl(120, 100%, 40%);">+ ; with extreme caution and only to mitigate specific issues.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Under certain conditions they could make things worse.</span><br><span> </span><br><span> ; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl</span><br><span> ;==========================ACL SECTION OPTIONS=========================</span><br><span>diff --git a/contrib/ast-db-manage/config/versions/f3c0b8695b66_taskprocessor_overload_trigger.py b/contrib/ast-db-manage/config/versions/f3c0b8695b66_taskprocessor_overload_trigger.py</span><br><span>new file mode 100644</span><br><span>index 0000000..6a5b9b2</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/f3c0b8695b66_taskprocessor_overload_trigger.py</span><br><span>@@ -0,0 +1,42 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""taskprocessor_overload_trigger</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: f3c0b8695b66</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: 0838f8db6a61</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2019-02-15 15:03:50.106790</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# revision identifiers, used by Alembic.</span><br><span style="color: hsl(120, 100%, 40%);">+revision = 'f3c0b8695b66'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '0838f8db6a61'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from alembic import op</span><br><span style="color: hsl(120, 100%, 40%);">+import sqlalchemy as sa</span><br><span style="color: hsl(120, 100%, 40%);">+from sqlalchemy.dialects.postgresql import ENUM</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_NAME = 'pjsip_taskprocessor_overload_trigger_values'</span><br><span style="color: hsl(120, 100%, 40%);">+PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_VALUES = ['none', 'global', 'pjsip_only']</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+ context = op.get_context()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if context.bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+ enum = ENUM(*PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+ name=PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+ enum.create(op.get_bind(), checkfirst=False)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ op.add_column('ps_globals',</span><br><span style="color: hsl(120, 100%, 40%);">+ sa.Column('taskprocessor_overload_trigger',</span><br><span style="color: hsl(120, 100%, 40%);">+ sa.Enum(*PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+ name=PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_NAME,</span><br><span style="color: hsl(120, 100%, 40%);">+ create_type=False)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def downgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+ if op.get_context().bind.dialect.name == 'mssql':</span><br><span style="color: hsl(120, 100%, 40%);">+ op.drop_constraint('ck_ps_globals_taskprocessor_overload_trigger_pjsip_taskprocessor_overload_trigger_values', 'ps_globals')</span><br><span style="color: hsl(120, 100%, 40%);">+ op.drop_column('ps_globals', 'taskprocessor_overload_trigger')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if context.bind.dialect.name == 'postgresql':</span><br><span style="color: hsl(120, 100%, 40%);">+ enum = ENUM(*PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_VALUES,</span><br><span style="color: hsl(120, 100%, 40%);">+ name=PJSIP_TASKPROCESSOR_OVERLOAD_TRIGGER_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+ enum.drop(op.get_bind(), checkfirst=False)</span><br><span>diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h</span><br><span>index f74989a..5278595 100644</span><br><span>--- a/include/asterisk/taskprocessor.h</span><br><span>+++ b/include/asterisk/taskprocessor.h</span><br><span>@@ -341,6 +341,19 @@</span><br><span> */</span><br><span> unsigned int ast_taskprocessor_alert_get(void);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the current taskprocessor high water alert count by sybsystem.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 13.26.0</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 16.3.0</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param subsystem The subsystem name</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 if no taskprocessors are in high water alert.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-zero if some task processors are in high water alert.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int ast_taskprocessor_get_subsystem_alert(const char *subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \brief Set the high and low alert water marks of the given taskprocessor queue.</span><br><span> * \since 13.10.0</span><br><span>diff --git a/main/sorcery.c b/main/sorcery.c</span><br><span>index 7b01374..3a43fa7 100644</span><br><span>--- a/main/sorcery.c</span><br><span>+++ b/main/sorcery.c</span><br><span>@@ -375,7 +375,7 @@</span><br><span> };</span><br><span> ast_assert(wizards == NULL);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- threadpool = ast_threadpool_create("Sorcery", NULL, &options);</span><br><span style="color: hsl(120, 100%, 40%);">+ threadpool = ast_threadpool_create("sorcery", NULL, &options);</span><br><span> if (!threadpool) {</span><br><span> return -1;</span><br><span> }</span><br><span>diff --git a/main/stasis.c b/main/stasis.c</span><br><span>index 5835a5a..fbcf37e 100644</span><br><span>--- a/main/stasis.c</span><br><span>+++ b/main/stasis.c</span><br><span>@@ -679,7 +679,7 @@</span><br><span> char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];</span><br><span> </span><br><span> /* Create name with seq number appended. */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "sub%c:%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "stasis/%c:%s",</span><br><span> use_thread_pool ? 'p' : 'm',</span><br><span> stasis_topic_name(topic));</span><br><span> </span><br><span>@@ -2595,7 +2595,7 @@</span><br><span> threadpool_opts.auto_increment = 1;</span><br><span> threadpool_opts.max_size = cfg->threadpool_options->max_size;</span><br><span> threadpool_opts.idle_timeout = cfg->threadpool_options->idle_timeout_sec;</span><br><span style="color: hsl(0, 100%, 40%);">- pool = ast_threadpool_create("stasis-core", NULL, &threadpool_opts);</span><br><span style="color: hsl(120, 100%, 40%);">+ pool = ast_threadpool_create("stasis", NULL, &threadpool_opts);</span><br><span> ao2_ref(cfg, -1);</span><br><span> if (!pool) {</span><br><span> ast_log(LOG_ERROR, "Failed to create 'stasis-core' threadpool\n");</span><br><span>diff --git a/main/taskprocessor.c b/main/taskprocessor.c</span><br><span>index 84c4d2b..9c04edd 100644</span><br><span>--- a/main/taskprocessor.c</span><br><span>+++ b/main/taskprocessor.c</span><br><span>@@ -91,7 +91,11 @@</span><br><span> unsigned int high_water_alert:1;</span><br><span> /*! Indicates if the taskprocessor is currently suspended */</span><br><span> unsigned int suspended:1;</span><br><span style="color: hsl(0, 100%, 40%);">- /*! \brief Friendly name of the taskprocessor */</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Anything before the first '/' in the name (if there is one) */</span><br><span style="color: hsl(120, 100%, 40%);">+ char *subsystem;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Friendly name of the taskprocessor.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Subsystem is appended after the name's NULL terminator.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span> char name[0];</span><br><span> };</span><br><span> </span><br><span>@@ -114,6 +118,16 @@</span><br><span> void *user_data;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * Keep track of which subsystems are in alert</span><br><span style="color: hsl(120, 100%, 40%);">+ * and how many of their taskprocessors are overloaded.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct subsystem_alert {</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int alert_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ char subsystem[0];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_VECTOR_RW(subsystem_alert_vector, struct subsystem_alert *) overloaded_subsystems;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #ifdef LOW_MEMORY</span><br><span> #define TPS_MAX_BUCKETS 61</span><br><span> #else</span><br><span>@@ -140,10 +154,12 @@</span><br><span> </span><br><span> static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);</span><br><span> static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);</span><br><span style="color: hsl(120, 100%, 40%);">+static char *cli_subsystem_alert_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);</span><br><span> </span><br><span> static struct ast_cli_entry taskprocessor_clis[] = {</span><br><span> AST_CLI_DEFINE(cli_tps_ping, "Ping a named task processor"),</span><br><span> AST_CLI_DEFINE(cli_tps_report, "List instantiated task processors and statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(cli_subsystem_alert_report, "List task processor subsystems in alert"),</span><br><span> };</span><br><span> </span><br><span> struct default_taskprocessor_listener_pvt {</span><br><span>@@ -273,6 +289,8 @@</span><br><span> static void tps_shutdown(void)</span><br><span> {</span><br><span> ast_cli_unregister_multiple(taskprocessor_clis, ARRAY_LEN(taskprocessor_clis));</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_CALLBACK_VOID(&overloaded_subsystems, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_FREE(&overloaded_subsystems);</span><br><span> ao2_t_ref(tps_singletons, -1, "Unref tps_singletons in shutdown");</span><br><span> tps_singletons = NULL;</span><br><span> }</span><br><span>@@ -287,6 +305,12 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_RW_INIT(&overloaded_subsystems, 10)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(tps_singletons, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "taskprocessor subsystems vector failed to initialize!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_cond_init(&cli_ping_cond, NULL);</span><br><span> </span><br><span> ast_cli_register_multiple(taskprocessor_clis, ARRAY_LEN(taskprocessor_clis));</span><br><span>@@ -550,6 +574,157 @@</span><br><span> return !strcasecmp(lhs->name, rhsname) ? CMP_MATCH | CMP_STOP : 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int subsystem_match(struct subsystem_alert *alert, const char *subsystem)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return !strcmp(alert->subsystem, subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int subsystem_cmp(struct subsystem_alert *a, struct subsystem_alert *b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return strcmp(a->subsystem, b->subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int ast_taskprocessor_get_subsystem_alert(const char *subsystem)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert *alert;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_RDLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ idx = AST_VECTOR_GET_INDEX(&overloaded_subsystems, subsystem, subsystem_match);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (idx >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ alert = AST_VECTOR_GET(&overloaded_subsystems, idx);</span><br><span style="color: hsl(120, 100%, 40%);">+ count = alert->alert_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return count;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void subsystem_alert_increment(const char *subsystem)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert *alert;</span><br><span style="color: hsl(120, 100%, 40%);">+ int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(subsystem)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_WRLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ idx = AST_VECTOR_GET_INDEX(&overloaded_subsystems, subsystem, subsystem_match);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (idx >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ alert = AST_VECTOR_GET(&overloaded_subsystems, idx);</span><br><span style="color: hsl(120, 100%, 40%);">+ alert->alert_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ alert = ast_malloc(sizeof(*alert) + strlen(subsystem) + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!alert) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ alert->alert_count = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(alert->subsystem, subsystem); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_APPEND(&overloaded_subsystems, alert)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(alert);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void subsystem_alert_decrement(const char *subsystem)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert *alert;</span><br><span style="color: hsl(120, 100%, 40%);">+ int idx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(subsystem)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_WRLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ idx = AST_VECTOR_GET_INDEX(&overloaded_subsystems, subsystem, subsystem_match);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (idx < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Can't decrement alert count for subsystem '%s' as it wasn't in alert\n", subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ alert = AST_VECTOR_GET(&overloaded_subsystems, idx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ alert->alert_count--;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert->alert_count <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&overloaded_subsystems, idx, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(alert);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void subsystem_copy(struct subsystem_alert *alert,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert_vector *vector)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert *alert_copy;</span><br><span style="color: hsl(120, 100%, 40%);">+ alert_copy = ast_malloc(sizeof(*alert_copy) + strlen(alert->subsystem) + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!alert_copy) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ alert_copy->alert_count = alert->alert_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(alert_copy->subsystem, alert->subsystem); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_ADD_SORTED(vector, alert_copy, subsystem_cmp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(alert_copy);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *cli_subsystem_alert_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert_vector sorted_subsystems;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_HEADERS_SUBSYSTEM "%-32s %12s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS_SUBSYSTEM "%-32s %12u\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "core show taskprocessor alerted subsystems";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: core show taskprocessor alerted subsystems\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Shows a list of task processor subsystems that are currently alerted\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != e->args) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT(&sorted_subsystems, AST_VECTOR_SIZE(&overloaded_subsystems))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_RDLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&overloaded_subsystems); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_copy(AST_VECTOR_GET(&overloaded_subsystems, i), &sorted_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_RW_UNLOCK(&overloaded_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n" FMT_HEADERS_SUBSYSTEM, "Subsystem", "Alert Count");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&sorted_subsystems); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct subsystem_alert *alert = AST_VECTOR_GET(&sorted_subsystems, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS_SUBSYSTEM, alert->subsystem, alert->alert_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n%lu subsystems\n\n", AST_VECTOR_SIZE(&sorted_subsystems));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_CALLBACK_VOID(&sorted_subsystems, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&sorted_subsystems);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! Count of the number of taskprocessors in high water alert. */</span><br><span> static unsigned int tps_alert_count;</span><br><span> </span><br><span>@@ -579,6 +754,15 @@</span><br><span> ast_log(LOG_DEBUG, "Taskprocessor '%s' %s the high water alert.\n",</span><br><span> tps->name, tps_alert_count ? "triggered" : "cleared");</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tps->subsystem[0] != '\0') {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (delta > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_alert_increment(tps->subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_alert_decrement(tps->subsystem);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_rwlock_unlock(&tps_alert_lock);</span><br><span> }</span><br><span> </span><br><span>@@ -749,8 +933,17 @@</span><br><span> static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, struct ast_taskprocessor_listener *listener)</span><br><span> {</span><br><span> struct ast_taskprocessor *p;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *subsystem_separator;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t subsystem_length = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t name_length;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- p = ao2_alloc(sizeof(*p) + strlen(name) + 1, tps_taskprocessor_dtor);</span><br><span style="color: hsl(120, 100%, 40%);">+ name_length = strlen(name);</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_separator = strchr(name, '/');</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_separator) {</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_length = subsystem_separator - name;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ p = ao2_alloc(sizeof(*p) + name_length + subsystem_length + 2, tps_taskprocessor_dtor);</span><br><span> if (!p) {</span><br><span> ast_log(LOG_WARNING, "failed to create taskprocessor '%s'\n", name);</span><br><span> return NULL;</span><br><span>@@ -760,7 +953,9 @@</span><br><span> p->tps_queue_low = (AST_TASKPROCESSOR_HIGH_WATER_LEVEL * 9) / 10;</span><br><span> p->tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- strcpy(p->name, name); /*SAFE*/</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(p->name, name); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+ p->subsystem = p->name + name_length + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(p->subsystem, name, subsystem_length + 1);</span><br><span> </span><br><span> ao2_ref(listener, +1);</span><br><span> p->listener = listener;</span><br><span>diff --git a/main/threadpool.c b/main/threadpool.c</span><br><span>index 2ab0936..56fbb2c 100644</span><br><span>--- a/main/threadpool.c</span><br><span>+++ b/main/threadpool.c</span><br><span>@@ -413,7 +413,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_str_set(&control_tps_name, 0, "%s-control", name);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&control_tps_name, 0, "%s/pool-control", name);</span><br><span> </span><br><span> pool->control_tps = ast_taskprocessor_get(ast_str_buffer(control_tps_name), TPS_REF_DEFAULT);</span><br><span> ast_free(control_tps_name);</span><br><span>@@ -919,6 +919,7 @@</span><br><span> struct ast_taskprocessor *tps;</span><br><span> RAII_VAR(struct ast_taskprocessor_listener *, tps_listener, NULL, ao2_cleanup);</span><br><span> RAII_VAR(struct ast_threadpool *, pool, NULL, ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *fullname;</span><br><span> </span><br><span> pool = threadpool_alloc(name, options);</span><br><span> if (!pool) {</span><br><span>@@ -935,7 +936,9 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- tps = ast_taskprocessor_create_with_listener(name, tps_listener);</span><br><span style="color: hsl(120, 100%, 40%);">+ fullname = ast_alloca(strlen(name) + strlen("/pool") + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ sprintf(fullname, "%s/pool", name); /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+ tps = ast_taskprocessor_create_with_listener(fullname, tps_listener);</span><br><span> if (!tps) {</span><br><span> return NULL;</span><br><span> }</span><br><span>diff --git a/res/res_pjsip.c b/res/res_pjsip.c</span><br><span>index 812c291..32277b1 100644</span><br><span>--- a/res/res_pjsip.c</span><br><span>+++ b/res/res_pjsip.c</span><br><span>@@ -1844,6 +1844,26 @@</span><br><span> <configOption name="send_contact_status_on_update_registration" default="yes"></span><br><span> <synopsis>Enable sending AMI ContactStatus event when a device refreshes its registration.</synopsis></span><br><span> </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="taskprocessor_overload_trigger"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Trigger scope for taskprocessor overloads</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description><para></span><br><span style="color: hsl(120, 100%, 40%);">+ This option specifies the trigger the distributor will use for</span><br><span style="color: hsl(120, 100%, 40%);">+ detecting taskprocessor overloads. When it detects an overload condition,</span><br><span style="color: hsl(120, 100%, 40%);">+ the distrubutor will stop accepting new requests until the overload is</span><br><span style="color: hsl(120, 100%, 40%);">+ cleared.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="global"><para>(default) Any taskprocessor overload will trigger.</para></enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="pjsip_only"><para>Only pjsip taskprocessor overloads will trigger.</para></enum></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="none"><para>No overload detection will be performed.</para></enum></span><br><span style="color: hsl(120, 100%, 40%);">+ </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <warning><para></span><br><span style="color: hsl(120, 100%, 40%);">+ The "none" and "pjsip_only" options should be used</span><br><span style="color: hsl(120, 100%, 40%);">+ with extreme caution and only to mitigate specific issues.</span><br><span style="color: hsl(120, 100%, 40%);">+ Under certain conditions they could make things worse.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></warning></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span> </configObject></span><br><span> </configFile></span><br><span> </configInfo></span><br><span>@@ -4267,7 +4287,7 @@</span><br><span> char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];</span><br><span> </span><br><span> /* Create name with seq number appended. */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip-group-serializer");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/group-serializer");</span><br><span> </span><br><span> return ast_sip_create_serializer_group_named(tps_name, shutdown_group);</span><br><span> }</span><br><span>@@ -4282,7 +4302,7 @@</span><br><span> char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];</span><br><span> </span><br><span> /* Create name with seq number appended. */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip-serializer");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/serializer");</span><br><span> </span><br><span> return ast_sip_create_serializer_group_named(tps_name, NULL);</span><br><span> }</span><br><span>@@ -4993,7 +5013,7 @@</span><br><span> /* The serializer needs threadpool and threadpool needs pjproject to be initialized so it's next */</span><br><span> sip_get_threadpool_options(&options);</span><br><span> options.thread_start = sip_thread_start;</span><br><span style="color: hsl(0, 100%, 40%);">- sip_threadpool = ast_threadpool_create("SIP", NULL, &options);</span><br><span style="color: hsl(120, 100%, 40%);">+ sip_threadpool = ast_threadpool_create("pjsip", NULL, &options);</span><br><span> if (!sip_threadpool) {</span><br><span> goto error;</span><br><span> }</span><br><span>diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c</span><br><span>index afeeb1d..365d9aa 100644</span><br><span>--- a/res/res_pjsip/config_global.c</span><br><span>+++ b/res/res_pjsip/config_global.c</span><br><span>@@ -51,6 +51,7 @@</span><br><span> #define DEFAULT_IGNORE_URI_USER_OPTIONS 0</span><br><span> #define DEFAULT_USE_CALLERID_CONTACT 0</span><br><span> #define DEFAULT_SEND_CONTACT_STATUS_ON_UPDATE_REGISTRATION 1</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL</span><br><span> </span><br><span> /*!</span><br><span> * \brief Cached global config object</span><br><span>@@ -110,6 +111,8 @@</span><br><span> unsigned int use_callerid_contact;</span><br><span> /*! Nonzero if need to send AMI ContactStatus event when a contact is updated */</span><br><span> unsigned int send_contact_status_on_update_registration;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Trigger the distributor should use to pause accepting new dialogs */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_sip_taskprocessor_overload_trigger overload_trigger;</span><br><span> };</span><br><span> </span><br><span> static void global_destructor(void *obj)</span><br><span>@@ -483,6 +486,58 @@</span><br><span> return send_contact_status_on_update_registration;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_sip_taskprocessor_overload_trigger ast_sip_get_taskprocessor_overload_trigger(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_sip_taskprocessor_overload_trigger trigger;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct global_config *cfg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg = get_global_cfg();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ trigger = cfg->overload_trigger;</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(cfg, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return trigger;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int overload_trigger_handler(const struct aco_option *opt,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct global_config *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(var->value, "none")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_NONE;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcasecmp(var->value, "global")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcasecmp(var->value, "pjsip_only")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ cfg->overload_trigger = TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unknown overload trigger '%s' specified for %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ var->value, var->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *overload_trigger_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [TASKPROCESSOR_OVERLOAD_TRIGGER_NONE] = "none",</span><br><span style="color: hsl(120, 100%, 40%);">+ [TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL] = "global",</span><br><span style="color: hsl(120, 100%, 40%);">+ [TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY] = "pjsip_only"</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_sip_overload_trigger_to_str(enum ast_sip_taskprocessor_overload_trigger trigger)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ARRAY_IN_BOUNDS(trigger, overload_trigger_map) ?</span><br><span style="color: hsl(120, 100%, 40%);">+ overload_trigger_map[trigger] : "";</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int overload_trigger_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct global_config *cfg = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf = ast_strdup(ast_sip_overload_trigger_to_str(cfg->overload_trigger));</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \internal</span><br><span> * \brief Observer to set default global object if none exist.</span><br><span>@@ -646,6 +701,9 @@</span><br><span> ast_sorcery_object_field_register(sorcery, "global", "send_contact_status_on_update_registration",</span><br><span> DEFAULT_SEND_CONTACT_STATUS_ON_UPDATE_REGISTRATION ? "yes" : "no",</span><br><span> OPT_YESNO_T, 1, FLDSET(struct global_config, send_contact_status_on_update_registration));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_object_field_register_custom(sorcery, "global", "taskprocessor_overload_trigger",</span><br><span style="color: hsl(120, 100%, 40%);">+ overload_trigger_map[DEFAULT_TASKPROCESSOR_OVERLOAD_TRIGGER],</span><br><span style="color: hsl(120, 100%, 40%);">+ overload_trigger_handler, overload_trigger_to_str, NULL, 0, 0);</span><br><span> </span><br><span> if (ast_sorcery_instance_observer_add(sorcery, &observer_callbacks_global)) {</span><br><span> return -1;</span><br><span>diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h</span><br><span>index 20d6ba4..273a4fa 100644</span><br><span>--- a/res/res_pjsip/include/res_pjsip_private.h</span><br><span>+++ b/res/res_pjsip/include/res_pjsip_private.h</span><br><span>@@ -480,4 +480,14 @@</span><br><span> */</span><br><span> int ast_sip_persistent_endpoint_add_to_regcontext(const char *regcontext);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_sip_taskprocessor_overload_trigger {</span><br><span style="color: hsl(120, 100%, 40%);">+ TASKPROCESSOR_OVERLOAD_TRIGGER_NONE = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+ TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_sip_taskprocessor_overload_trigger ast_sip_get_taskprocessor_overload_trigger(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_sip_overload_trigger_to_str(enum ast_sip_taskprocessor_overload_trigger trigger);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* RES_PJSIP_PRIVATE_H_ */</span><br><span>diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c</span><br><span>index d67b942..94f8e17 100644</span><br><span>--- a/res/res_pjsip/pjsip_distributor.c</span><br><span>+++ b/res/res_pjsip/pjsip_distributor.c</span><br><span>@@ -50,6 +50,7 @@</span><br><span> static unsigned int unidentified_period;</span><br><span> static unsigned int unidentified_prune_interval;</span><br><span> static int using_auth_username;</span><br><span style="color: hsl(120, 100%, 40%);">+static enum ast_sip_taskprocessor_overload_trigger overload_trigger;</span><br><span> </span><br><span> struct unidentified_request{</span><br><span> struct timeval first_seen;</span><br><span>@@ -524,7 +525,10 @@</span><br><span> ao2_cleanup(dist);</span><br><span> return PJ_TRUE;</span><br><span> } else {</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_taskprocessor_alert_get()) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((overload_trigger == TASKPROCESSOR_OVERLOAD_TRIGGER_GLOBAL &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_alert_get())</span><br><span style="color: hsl(120, 100%, 40%);">+ || (overload_trigger == TASKPROCESSOR_OVERLOAD_TRIGGER_PJSIP_ONLY &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_get_subsystem_alert("pjsip"))) {</span><br><span> /*</span><br><span> * When taskprocessors get backed up, there is a good chance that</span><br><span> * we are being overloaded and need to defer adding new work to</span><br><span>@@ -1186,6 +1190,8 @@</span><br><span> </span><br><span> ast_sip_get_unidentified_request_thresholds(&unidentified_count, &unidentified_period, &unidentified_prune_interval);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ overload_trigger = ast_sip_get_taskprocessor_overload_trigger();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Clean out the old task, if any */</span><br><span> ast_sched_clean_by_callback(prune_context, prune_task, clean_task);</span><br><span> /* Have to do something with the return value to shut up the stupid compiler. */</span><br><span>diff --git a/tests/test_taskprocessor.c b/tests/test_taskprocessor.c</span><br><span>index 6428746..70cb556 100644</span><br><span>--- a/tests/test_taskprocessor.c</span><br><span>+++ b/tests/test_taskprocessor.c</span><br><span>@@ -46,6 +46,8 @@</span><br><span> ast_mutex_t lock;</span><br><span> /*! Boolean indicating that the task was run */</span><br><span> int task_complete;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Milliseconds to wait before returning */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned long wait_time;</span><br><span> };</span><br><span> </span><br><span> static void task_data_dtor(void *obj)</span><br><span>@@ -69,6 +71,7 @@</span><br><span> ast_cond_init(&task_data->cond, NULL);</span><br><span> ast_mutex_init(&task_data->lock);</span><br><span> task_data->task_complete = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ task_data->wait_time = 0;</span><br><span> </span><br><span> return task_data;</span><br><span> }</span><br><span>@@ -83,7 +86,11 @@</span><br><span> static int task(void *data)</span><br><span> {</span><br><span> struct task_data *task_data = data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> SCOPED_MUTEX(lock, &task_data->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (task_data->wait_time > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ usleep(task_data->wait_time * 1000);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> task_data->task_complete = 1;</span><br><span> ast_cond_signal(&task_data->cond);</span><br><span> return 0;</span><br><span>@@ -165,6 +172,143 @@</span><br><span> return AST_TEST_PASS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Baseline test for subsystem alert</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(subsystem_alert)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_taskprocessor *, tps, NULL, ast_taskprocessor_unreference);</span><br><span style="color: hsl(120, 100%, 40%);">+#define TEST_DATA_ARRAY_SIZE 10</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOW_WATER_MARK 3</span><br><span style="color: hsl(120, 100%, 40%);">+#define HIGH_WATER_MARK 6</span><br><span style="color: hsl(120, 100%, 40%);">+ struct task_data *task_data[(TEST_DATA_ARRAY_SIZE + 1)] = { 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ long queue_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int alert_level;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int subsystem_alert_level;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = "subsystem_alert";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = "/main/taskprocessor/";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test of subsystem alerts";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Ensures alerts are generated properly.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ tps = ast_taskprocessor_get("test_subsystem/test", TPS_REF_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!tps) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Unable to create test taskprocessor\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_alert_set_levels(tps, LOW_WATER_MARK, HIGH_WATER_MARK);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_suspend(tps);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 1; i <= TEST_DATA_ARRAY_SIZE; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ task_data[i] = task_data_create();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!task_data[i]) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Unable to create task_data\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto data_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ task_data[i]->wait_time = 500;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Pushing task %d\n", i);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_taskprocessor_push(tps, task, task_data[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to queue task\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto data_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ queue_count = ast_taskprocessor_size(tps);</span><br><span style="color: hsl(120, 100%, 40%);">+ alert_level = ast_taskprocessor_alert_get();</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_alert_level = ast_taskprocessor_get_subsystem_alert("test_subsystem");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (queue_count == HIGH_WATER_MARK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_alert_level) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert triggered correctly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert_level) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert triggered correctly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (queue_count < HIGH_WATER_MARK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_alert_level > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert triggered unexpectedly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert_level > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert triggered unexpectedly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_alert_level == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert failed to trigger at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert_level == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert failed to trigger at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_taskprocessor_unsuspend(tps);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 1; i <= TEST_DATA_ARRAY_SIZE; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Waiting on task %d\n", i);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (task_wait(task_data[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Queued task '%d' did not execute!\n", i);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto data_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ queue_count = ast_taskprocessor_size(tps);</span><br><span style="color: hsl(120, 100%, 40%);">+ alert_level = ast_taskprocessor_alert_get();</span><br><span style="color: hsl(120, 100%, 40%);">+ subsystem_alert_level = ast_taskprocessor_get_subsystem_alert("test_subsystem");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (queue_count == LOW_WATER_MARK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!subsystem_alert_level) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert cleared correctly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!alert_level) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert cleared correctly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (queue_count > LOW_WATER_MARK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_alert_level == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert cleared unexpectedly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert_level == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert cleared unexpectedly at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (subsystem_alert_level > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Subsystem alert failed to clear at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (alert_level > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Global alert failed to clear at %ld\n", queue_count);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+data_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 1; i <= TEST_DATA_ARRAY_SIZE; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(task_data[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res ? AST_TEST_FAIL : AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define NUM_TASKS 20000</span><br><span> </span><br><span> /*!</span><br><span>@@ -749,6 +893,7 @@</span><br><span> {</span><br><span> ast_test_unregister(default_taskprocessor);</span><br><span> ast_test_unregister(default_taskprocessor_load);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_unregister(subsystem_alert);</span><br><span> ast_test_unregister(taskprocessor_listener);</span><br><span> ast_test_unregister(taskprocessor_shutdown);</span><br><span> ast_test_unregister(taskprocessor_push_local);</span><br><span>@@ -759,6 +904,7 @@</span><br><span> {</span><br><span> ast_test_register(default_taskprocessor);</span><br><span> ast_test_register(default_taskprocessor_load);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_register(subsystem_alert);</span><br><span> ast_test_register(taskprocessor_listener);</span><br><span> ast_test_register(taskprocessor_shutdown);</span><br><span> ast_test_register(taskprocessor_push_local);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/11001">change 11001</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/11001"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I8c19068bb2fc26610a9f0b8624bdf577a04fcd56 </div>
<div style="display:none"> Gerrit-Change-Number: 11001 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation (1000185) </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua C. Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Richard Mudgett <rmudgett@digium.com> </div>