diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 6e4d936..114a9a8 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1082,6 +1082,11 @@ static struct ao2_container *dialogs; #define sip_pvt_lock(x) ao2_lock(x) #define sip_pvt_trylock(x) ao2_trylock(x) #define sip_pvt_unlock(x) ao2_unlock(x) +/*! \brief + * This container holds all dialogs that have been unlinked from the dialogs + * container, but still need to free all of the references contained inside. + */ +static struct ao2_container *dialogs_to_unlink; /*! \brief The table of TCP threads */ static struct ao2_container *threadt; @@ -2920,19 +2925,37 @@ static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy) } /*! - * \brief Unlink a dialog from the dialogs container, as well as any other places - * that it may be currently stored. + * \brief Unlink a dialog from the dialogs container and schedule a call to + * dialog_unlink_references. + * + * \note A reference to the dialog must be held before calling this function, + * and this function does not release that reference. + */ +void *dialog_unlink_all_later(struct sip_pvt *dialog) +{ + ao2_t_link(dialogs_to_unlink, dialog, "linking dialog via ao2_link"); + ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink"); + return NULL; +} + +void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist) +{ + return dialog_unlink_all_body(dialogs, dialog, lockowner, lockdialoglist); +} + +/*! + * \brief Unlink a dialog from any places that it may be currently stored. * * \note A reference to the dialog must be held before calling this function, and this * function does not release that reference. */ -void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist) +void *dialog_unlink_all_body(struct ao2_container *container, struct sip_pvt *dialog, int lockowner, int lockdialoglist) { struct sip_pkt *cp; dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done"); - ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink"); + ao2_t_unlink(container, dialog, "unlinking via ao2_unlink"); /* Unlink us from the owner (channel) if we have one */ if (dialog->owner) { @@ -16309,6 +16332,8 @@ static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_a ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), ®l); ast_cli(a->fd, "-= Dialog objects:\n\n"); ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, a, "initiate ao2_callback to dump dialogs"); + ast_cli(a->fd, "-= Destroyed (but not unlinked) dialog objects:\n\n"); + ao2_t_callback(dialogs_to_unlink, OBJ_NODATA, dialog_dump_func, a, "initiate ao2_callback to dump destroyed dialogs"); return CLI_SUCCESS; } /*! \brief Print call group and pickup group */ @@ -16439,7 +16464,7 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) sip_pvt_unlock(dialog); /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */ /* the CMP_MATCH will unlink this dialog from the dialog hash table */ - dialog_unlink_all(dialog, TRUE, FALSE); + dialog_unlink_all_later(dialog); return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */ } @@ -16448,6 +16473,15 @@ static int dialog_needdestroy(void *dialogobj, void *arg, int flags) return 0; } +static int dialog_unlink_destroyed(void *dialogobj, void *arg, int flags) +{ + struct sip_pvt *dialog = dialogobj; + + dialog_unlink_all_body(dialogs_to_unlink, dialog, TRUE, FALSE); + + return 0; +} + /*! \brief Remove temporary realtime objects from memory (CLI) */ /*! \todo XXXX Propably needs an overhaul after removal of the devices */ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) @@ -25027,7 +25061,7 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) static void *do_monitor(void *data) { int res; - time_t t; + time_t t, last_destroy_time = 0; int reloading; /* Add an I/O event to our SIP UDP socket */ @@ -25063,8 +25097,13 @@ static void *do_monitor(void *data) of time since the last time we did it (when MWI is being sent, we can get back to this point every millisecond or less) */ - ao2_t_callback(dialogs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy, &t, - "callback to remove dialogs w/needdestroy"); + if (t > last_destroy_time) { + last_destroy_time = t; + ao2_t_callback(dialogs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, dialog_needdestroy, &t, + "callback to remove dialogs w/needdestroy"); + ao2_t_callback(dialogs_to_unlink, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, dialog_unlink_destroyed, NULL, + "callback to remove dialogs found by dialog_needdestroy"); + } /* the old methodology would be to restart the search for dialogs to delete with every dialog that was found and destroyed, probably because the list contents would change, @@ -29435,6 +29474,7 @@ static int load_module(void) peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers"); peers_by_ip = ao2_t_container_alloc(HASH_PEER_SIZE, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip"); dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs"); + dialogs_to_unlink = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs_to_unlink"); threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table"); if (!peers || !peers_by_ip || !dialogs || !threadt) { ast_log(LOG_ERROR, "Unable to create primary SIP container(s)\n"); @@ -29657,6 +29697,14 @@ static int unload_module(void) ao2_t_ref(p, -1, "throw away iterator result"); } ao2_iterator_destroy(&i); + + /* Finish unlinking all the dialogs */ + i = ao2_iterator_init(dialogs_to_unlink, 0); + while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs_to_unlink"))) { + dialog_unlink_all_body(dialogs_to_unlink, p, TRUE, FALSE); + ao2_t_ref(p, -1, "throw away iterator result"); + } + ao2_iterator_destroy(&i); /* Free memory for local network address mask */ ast_free_ha(localaddr); @@ -29694,6 +29742,7 @@ static int unload_module(void) ao2_t_ref(peers, -1, "unref the peers table"); ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table"); ao2_t_ref(dialogs, -1, "unref the dialogs table"); + ao2_t_ref(dialogs_to_unlink, -1, "unref the dialogs_to_unlink table"); ao2_t_ref(threadt, -1, "unref the thread table"); ao2_t_ref(sip_monitor_instances, -1, "unref the sip_monitor_instances table"); diff --git a/channels/sip/include/dialog.h b/channels/sip/include/dialog.h index ed31b77..b372c9e 100644 --- a/channels/sip/include/dialog.h +++ b/channels/sip/include/dialog.h @@ -61,6 +61,8 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist); * function does not release that reference. */ void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist); +void *dialog_unlink_all_later(struct sip_pvt *dialog); +void *dialog_unlink_all_body(struct ao2_container *container, struct sip_pvt *dialog, int lockowner, int lockdialoglist); /*! \brief Acknowledges receipt of a packet and stops retransmission * called with p locked*/ -- 1.7.6