<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>