summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/res_pjsip_session.c65
1 files changed, 59 insertions, 6 deletions
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 8c04a7cfb..2c2ea616d 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -2247,7 +2247,7 @@ static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdat
}
}
-static int session_end(struct ast_sip_session *session)
+static void session_end(struct ast_sip_session *session)
{
struct ast_sip_session_supplement *iter;
@@ -2256,14 +2256,28 @@ static int session_end(struct ast_sip_session *session)
ao2_ref(session, -1);
}
- /* Session is dead. Let's get rid of the reference to the session */
+ /* Session is dead. Notify the supplements. */
AST_LIST_TRAVERSE(&session->supplements, iter, next) {
if (iter->session_end) {
iter->session_end(session);
}
}
+}
+
+/*!
+ * \internal
+ * \brief Complete ending session activities.
+ * \since 13.5.0
+ *
+ * \param vsession Which session to complete stopping.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int session_end_completion(void *vsession)
+{
+ struct ast_sip_session *session = vsession;
- session->inv_session->mod_data[session_module.id] = NULL;
ast_sip_dialog_set_serializer(session->inv_session->dlg, NULL);
ast_sip_dialog_set_endpoint(session->inv_session->dlg, NULL);
ao2_cleanup(session);
@@ -2340,9 +2354,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
print_debug_details(inv, tsx, e);
if (!session) {
- /* Transaction likely timed out after the call was hung up. Just
- * ignore such transaction changes
- */
+ /* The session has ended. Ignore the transaction change. */
return;
}
switch (e->body.tsx_state.type) {
@@ -2423,7 +2435,48 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
}
break;
case PJSIP_EVENT_TRANSPORT_ERROR:
+ /*
+ * Clear the module data now to block session_inv_on_state_changed()
+ * from calling session_end() if it hasn't already done so.
+ */
+ inv->mod_data[session_module.id] = NULL;
+
+ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+ session_end(session);
+ }
+
+ /*
+ * Pass the session ref held by session->inv_session to
+ * session_end_completion().
+ */
+ session_end_completion(session);
+ return;
case PJSIP_EVENT_TIMER:
+ /*
+ * The timer event is run by the pjsip monitor thread and not
+ * by the session serializer.
+ */
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ /*
+ * We are locking because ast_sip_dialog_get_session() needs
+ * the dialog locked to get the session by other threads.
+ */
+ pjsip_dlg_inc_lock(inv->dlg);
+ session = inv->mod_data[session_module.id];
+ inv->mod_data[session_module.id] = NULL;
+ pjsip_dlg_dec_lock(inv->dlg);
+
+ /*
+ * Pass the session ref held by session->inv_session to
+ * session_end_completion().
+ */
+ if (ast_sip_push_task(session->serializer, session_end_completion, session)) {
+ /* Do it anyway even though this is not the right thread. */
+ session_end_completion(session);
+ }
+ return;
+ }
+ break;
case PJSIP_EVENT_USER:
case PJSIP_EVENT_UNKNOWN:
case PJSIP_EVENT_TSX_STATE: