diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c index 8ba8c2da2615c0098c89c5a1537df630dd6a3ed9..1cf8e50466e09780fb97ef2beb244b67d234e85f 100644 --- a/res/res_pjsip_transport_management.c +++ b/res/res_pjsip_transport_management.c @@ -42,7 +42,7 @@ static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 }; /*! \brief Global container of active transports */ -static struct ao2_container *transports; +static AO2_GLOBAL_OBJ_STATIC(monitored_transports); /*! \brief Scheduler context for timing out connections with no data received */ static struct ast_sched_context *sched; @@ -84,6 +84,7 @@ static int keepalive_transport_cb(void *obj, void *arg, int flags) /*! \brief Thread which sends keepalives to all active connection-oriented transports */ static void *keepalive_transport_thread(void *data) { + struct ao2_container *transports; pj_thread_desc desc; pj_thread_t *thread; @@ -92,6 +93,11 @@ static void *keepalive_transport_thread(void *data) return NULL; } + transports = ao2_global_obj_ref(monitored_transports); + if (!transports) { + return NULL; + } + /* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying * callback for the transport manager. */ @@ -100,6 +106,7 @@ static void *keepalive_transport_thread(void *data) ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL); } + ao2_ref(transports, -1); return NULL; } @@ -108,7 +115,6 @@ AST_THREADSTORAGE(desc_storage); static int idle_sched_cb(const void *data) { struct monitored_transport *keepalive = (struct monitored_transport *) data; - int sip_received = ast_atomic_fetchadd_int(&keepalive->sip_received, 0); if (!pj_thread_is_registered()) { pj_thread_t *thread; @@ -126,7 +132,7 @@ static int idle_sched_cb(const void *data) pj_thread_register("Transport Monitor", *desc, &thread); } - if (!sip_received) { + if (!keepalive->sip_received) { ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n", keepalive->transport->info, IDLE_TIMEOUT); pjsip_transport_shutdown(keepalive->transport); @@ -148,23 +154,30 @@ static void monitored_transport_destroy(void *obj) static void monitored_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state, const pjsip_transport_state_info *info) { + struct ao2_container *transports; + /* We only care about reliable transports */ - if (PJSIP_TRANSPORT_IS_RELIABLE(transport) && - (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval)) { + if (PJSIP_TRANSPORT_IS_RELIABLE(transport) + && (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval) + && (transports = ao2_global_obj_ref(monitored_transports))) { struct monitored_transport *monitored; switch (state) { case PJSIP_TP_STATE_CONNECTED: - monitored = ao2_alloc(sizeof(*monitored), monitored_transport_destroy); + monitored = ao2_alloc_options(sizeof(*monitored), + monitored_transport_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!monitored) { break; } monitored->transport = transport; pjsip_transport_add_ref(monitored->transport); + ao2_link(transports, monitored); + if (transport->dir == PJSIP_TP_DIR_INCOMING) { /* Let the scheduler inherit the reference from allocation */ if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) { + /* Uh Oh. Could not schedule the idle check. Kill the transport. */ ao2_unlink(transports, monitored); ao2_ref(monitored, -1); pjsip_transport_shutdown(transport); @@ -181,6 +194,8 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip default: break; } + + ao2_ref(transports, -1); } /* Forward to the old state callback if present */ @@ -242,7 +257,7 @@ static int monitored_transport_cmp_fn(void *obj, void *arg, int flags) break; } - return !cmp ? CMP_MATCH | CMP_STOP : 0; + return !cmp ? CMP_MATCH : 0; } static void keepalive_global_loaded(const char *object_type) @@ -265,8 +280,8 @@ static void keepalive_global_loaded(const char *object_type) if (ast_pthread_create(&keepalive_thread, NULL, keepalive_transport_thread, NULL)) { ast_log(LOG_ERROR, "Could not create thread for sending keepalive messages.\n"); - ao2_ref(transports, -1); - return; + keepalive_thread = AST_PTHREADT_NULL; + keepalive_interval = 0; } } @@ -283,14 +298,21 @@ static struct ast_sorcery_observer keepalive_global_observer = { */ static pj_bool_t idle_monitor_on_rx_request(pjsip_rx_data *rdata) { + struct ao2_container *transports; struct monitored_transport *idle_trans; + transports = ao2_global_obj_ref(monitored_transports); + if (!transports) { + return PJ_FALSE; + } + idle_trans = ao2_find(transports, rdata->tp_info.transport->obj_name, OBJ_SEARCH_KEY); + ao2_ref(transports, -1); if (!idle_trans) { return PJ_FALSE; } - ast_atomic_fetchadd_int(&idle_trans->sip_received, +1); + idle_trans->sip_received = 1; ao2_ref(idle_trans, -1); return PJ_FALSE; @@ -304,35 +326,38 @@ static pjsip_module idle_monitor_module = { static int load_module(void) { + struct ao2_container *transports; pjsip_tpmgr *tpmgr; CHECK_PJSIP_MODULE_LOADED(); + tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()); + if (!tpmgr) { + ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n"); + return AST_MODULE_LOAD_DECLINE; + } + transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn, monitored_transport_cmp_fn); if (!transports) { ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n"); return AST_MODULE_LOAD_DECLINE; } - - tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()); - if (!tpmgr) { - ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n"); - ao2_ref(transports, -1); - return AST_MODULE_LOAD_DECLINE; - } + ao2_global_obj_replace_unref(monitored_transports, transports); + ao2_ref(transports, -1); sched = ast_sched_context_create(); if (!sched) { ast_log(LOG_ERROR, "Failed to create keepalive scheduler context.\n"); - ao2_ref(transports, -1); + ao2_global_obj_release(monitored_transports); return AST_MODULE_LOAD_DECLINE; } if (ast_sched_start_thread(sched)) { ast_log(LOG_ERROR, "Failed to start keepalive scheduler thread\n"); ast_sched_context_destroy(sched); - ao2_ref(transports, -1); + sched = NULL; + ao2_global_obj_release(monitored_transports); return AST_MODULE_LOAD_DECLINE; } @@ -343,25 +368,38 @@ static int load_module(void) ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer); ast_sorcery_reload_object(ast_sip_get_sorcery(), "global"); + ast_module_shutdown_ref(ast_module_info->self); return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { - pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()); + pjsip_tpmgr *tpmgr; if (keepalive_interval) { keepalive_interval = 0; - pthread_kill(keepalive_thread, SIGURG); - pthread_join(keepalive_thread, NULL); + if (keepalive_thread != AST_PTHREADT_NULL) { + pthread_kill(keepalive_thread, SIGURG); + pthread_join(keepalive_thread, NULL); + keepalive_thread = AST_PTHREADT_NULL; + } } - ast_sched_context_destroy(sched); - ao2_ref(transports, -1); + ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer); + + tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()); + if (tpmgr) { + pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback); + } ast_sip_unregister_service(&idle_monitor_module); - pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback); + + ast_sched_context_destroy(sched); + sched = NULL; + + ao2_global_obj_release(monitored_transports); + return 0; }