chan_sip: Reorder unload_module to deal with stuck TCP threads.
authorCorey Farrell <git@cfware.com>
Fri, 9 Dec 2016 02:00:02 +0000 (21:00 -0500)
committerCorey Farrell <git@cfware.com>
Sat, 17 Dec 2016 16:25:40 +0000 (11:25 -0500)
In some situations TCP threads may become frozen.  This creates the
possibility that Asterisk could segfault if they become unfrozen after
chan_sip has been dlclose'd.  This reorders the unload_module process to
allow abort if threads do not exit within 5 seconds.

High level order as follows:
1) Unregister from the core to stop new requests.
2) Signal threads to stop
3) Clear config based tables (but do not free the table itself).
4) Verify that threads have shutdown, cancel unload if not.
5) Clean all remaining resources.

ASTERISK-26586

Change-Id: Ie23692041d838fbd35ece61868f4c640960ff882

channels/chan_sip.c

index aa0d6a5..af17a38 100644 (file)
@@ -1780,7 +1780,7 @@ static void destroy_escs(void)
 {
        int i;
        for (i = 0; i < ARRAY_LEN(event_state_compositors); i++) {
-               ao2_cleanup(event_state_compositors[i].compositor);
+               ao2_replace(event_state_compositors[i].compositor, NULL);
        }
 }
 
@@ -35365,6 +35365,8 @@ static int unload_module(void)
        struct ao2_iterator i;
        struct timeval start;
 
+       ast_sched_dump(sched);
+
        ast_sip_api_provider_unregister();
 
        if (sip_cfg.websocket_enabled) {
@@ -35374,12 +35376,11 @@ static int unload_module(void)
        network_change_stasis_unsubscribe();
        acl_change_event_stasis_unsubscribe();
 
-       ast_sched_dump(sched);
-
        /* First, take us out of the channel type list */
        ast_channel_unregister(&sip_tech);
-
        ast_msg_tech_unregister(&sip_msg_tech);
+       ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
+       ast_cc_agent_unregister(&sip_cc_agent_callbacks);
 
        /* Unregister dial plan functions */
        ast_custom_function_unregister(&sippeer_function);
@@ -35443,8 +35444,6 @@ static int unload_module(void)
        }
        ao2_iterator_destroy(&i);
 
-       unlink_all_peers_from_tables();
-
        ast_mutex_lock(&monlock);
        if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
                pthread_t th = monitor_thread;
@@ -35458,7 +35457,12 @@ static int unload_module(void)
                ast_mutex_unlock(&monlock);
        }
 
+       /* Clear containers */
+       unlink_all_peers_from_tables();
        cleanup_all_regs();
+       sip_epa_unregister_all();
+       destroy_escs();
+       clear_sip_domains();
 
        {
                struct ao2_iterator iter;
@@ -35487,6 +35491,23 @@ static int unload_module(void)
         */
        ast_sched_runq(sched);
 
+       /*
+        * Wait awhile for the TCP/TLS thread container to become empty.
+        *
+        * XXX This is a hack, but the worker threads cannot be created
+        * joinable.  They can die on their own and remove themselves
+        * from the container thus resulting in a huge memory leak.
+        */
+       start = ast_tvnow();
+       while (ao2_container_count(threadt) && (ast_tvdiff_sec(ast_tvnow(), start) < 5)) {
+               sched_yield();
+       }
+       if (ao2_container_count(threadt)) {
+               ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
+
+               return -1;
+       }
+
        /* Free memory for local network address mask */
        ast_free_ha(localaddr);
 
@@ -35497,9 +35518,6 @@ static int unload_module(void)
        }
        ast_mutex_unlock(&authl_lock);
 
-       sip_epa_unregister_all();
-       destroy_escs();
-
        ast_free(default_tls_cfg.certfile);
        ast_free(default_tls_cfg.pvtfile);
        ast_free(default_tls_cfg.cipher);
@@ -35508,21 +35526,6 @@ static int unload_module(void)
 
        ast_rtp_dtls_cfg_free(&default_dtls_cfg);
 
-       /*
-        * Wait awhile for the TCP/TLS thread container to become empty.
-        *
-        * XXX This is a hack, but the worker threads cannot be created
-        * joinable.  They can die on their own and remove themselves
-        * from the container thus resulting in a huge memory leak.
-        */
-       start = ast_tvnow();
-       while (ao2_container_count(threadt) && (ast_tvdiff_sec(ast_tvnow(), start) < 5)) {
-               sched_yield();
-       }
-       if (ao2_container_count(threadt)) {
-               ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
-       }
-
        ao2_cleanup(registry_list);
        ao2_cleanup(subscription_mwi_list);
 
@@ -35536,7 +35539,6 @@ static int unload_module(void)
        ao2_t_cleanup(threadt, "unref the thread table");
        ao2_t_cleanup(sip_monitor_instances, "unref the sip_monitor_instances table");
 
-       clear_sip_domains();
        sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
        if (sipsock_read_id) {
                ast_io_remove(io, sipsock_read_id);
@@ -35549,8 +35551,6 @@ static int unload_module(void)
        ast_context_destroy_by_name(used_context, "SIP");
        ast_unload_realtime("sipregs");
        ast_unload_realtime("sippeers");
-       ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
-       ast_cc_agent_unregister(&sip_cc_agent_callbacks);
 
        sip_reqresp_parser_exit();
        sip_unregister_tests();