Merge "res_pjsip_pubsub: Solidify lifetime and ownership of objects."

This commit is contained in:
Matt Jordan 2015-10-25 10:11:06 -05:00 committed by Gerrit Code Review
commit fe52fa5cc3
5 changed files with 227 additions and 79 deletions

View File

@ -684,6 +684,15 @@ const char *ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
*/ */
const char *ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub); const char *ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub);
/*!
* \since 13.6.0
* \brief Alert the pubsub core that the subscription is ready for destruction
*
* \param sub The subscription that is complete
* \return Nothing
*/
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub);
/*! \brief Determines whether the res_pjsip_pubsub module is loaded */ /*! \brief Determines whether the res_pjsip_pubsub module is loaded */
#define CHECK_PJSIP_PUBSUB_MODULE_LOADED() \ #define CHECK_PJSIP_PUBSUB_MODULE_LOADED() \
do { \ do { \

View File

@ -115,7 +115,7 @@ static void exten_state_subscription_destructor(void *obj)
struct exten_state_subscription *sub = obj; struct exten_state_subscription *sub = obj;
ast_free(sub->user_agent); ast_free(sub->user_agent);
ao2_cleanup(sub->sip_sub); ast_sip_subscription_destroy(sub->sip_sub);
ast_taskprocessor_unreference(sub->serializer); ast_taskprocessor_unreference(sub->serializer);
} }
@ -160,7 +160,7 @@ static struct exten_state_subscription *exten_state_subscription_alloc(
return NULL; return NULL;
} }
exten_state_sub->sip_sub = ao2_bump(sip_sub); exten_state_sub->sip_sub = sip_sub;
/* We keep our own reference to the serializer as there is no guarantee in state_changed /* We keep our own reference to the serializer as there is no guarantee in state_changed
* that the subscription tree is still valid when it is called. This can occur when * that the subscription tree is still valid when it is called. This can occur when

View File

@ -204,7 +204,9 @@ static void mwi_subscription_destructor(void *obj)
struct mwi_subscription *sub = obj; struct mwi_subscription *sub = obj;
ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id); ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id);
ao2_cleanup(sub->sip_sub); if (sub->is_solicited) {
ast_sip_subscription_destroy(sub->sip_sub);
}
ao2_cleanup(sub->stasis_subs); ao2_cleanup(sub->stasis_subs);
ast_free(sub->aors); ast_free(sub->aors);
} }
@ -233,7 +235,7 @@ static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *
* state not being updated on the device * state not being updated on the device
*/ */
if (is_solicited) { if (is_solicited) {
sub->sip_sub = ao2_bump(sip_sub); sub->sip_sub = sip_sub;
} }
sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp); sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp);

View File

@ -411,6 +411,8 @@ struct sip_subscription_tree {
int is_list; int is_list;
/*! Next item in the list */ /*! Next item in the list */
AST_LIST_ENTRY(sip_subscription_tree) next; AST_LIST_ENTRY(sip_subscription_tree) next;
/*! Indicates that a NOTIFY is currently being sent on the SIP subscription */
int last_notify;
}; };
/*! /*!
@ -1063,14 +1065,28 @@ static void remove_subscription(struct sip_subscription_tree *obj)
AST_RWLIST_TRAVERSE_SAFE_END; AST_RWLIST_TRAVERSE_SAFE_END;
} }
static void subscription_destructor(void *obj) static void destroy_subscription(struct ast_sip_subscription *sub)
{ {
struct ast_sip_subscription *sub = obj;
ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource); ast_debug(3, "Destroying SIP subscription to resource %s\n", sub->resource);
ast_free(sub->body_text); ast_free(sub->body_text);
AST_VECTOR_FREE(&sub->children);
ao2_cleanup(sub->datastores); ao2_cleanup(sub->datastores);
ast_free(sub);
}
static void destroy_subscriptions(struct ast_sip_subscription *root)
{
int i;
for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
struct ast_sip_subscription *child;
child = AST_VECTOR_GET(&root->children, i);
destroy_subscriptions(child);
}
destroy_subscription(root);
} }
static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler, static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_subscription_handler *handler,
@ -1079,7 +1095,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
struct ast_sip_subscription *sub; struct ast_sip_subscription *sub;
pjsip_sip_uri *contact_uri; pjsip_sip_uri *contact_uri;
sub = ao2_alloc(sizeof(*sub) + strlen(resource) + 1, subscription_destructor); sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
if (!sub) { if (!sub) {
return NULL; return NULL;
} }
@ -1087,13 +1103,13 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp); sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
if (!sub->datastores) { if (!sub->datastores) {
ao2_ref(sub, -1); destroy_subscription(sub);
return NULL; return NULL;
} }
sub->body_text = ast_str_create(128); sub->body_text = ast_str_create(128);
if (!sub->body_text) { if (!sub->body_text) {
ao2_ref(sub, -1); destroy_subscription(sub);
return NULL; return NULL;
} }
@ -1104,7 +1120,7 @@ static struct ast_sip_subscription *allocate_subscription(const struct ast_sip_s
sub->handler = handler; sub->handler = handler;
sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE; sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
sub->tree = tree; sub->tree = ao2_bump(tree);
return sub; return sub;
} }
@ -1132,6 +1148,7 @@ static struct ast_sip_subscription *create_virtual_subscriptions(const struct as
sub->full_state = current->full_state; sub->full_state = current->full_state;
sub->body_generator = generator; sub->body_generator = generator;
AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) { for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
struct ast_sip_subscription *child; struct ast_sip_subscription *child;
@ -1166,7 +1183,6 @@ static void shutdown_subscriptions(struct ast_sip_subscription *sub)
if (AST_VECTOR_SIZE(&sub->children) > 0) { if (AST_VECTOR_SIZE(&sub->children) > 0) {
for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) { for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i)); shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
ao2_cleanup(AST_VECTOR_GET(&sub->children, i));
} }
return; return;
} }
@ -1181,6 +1197,8 @@ static void subscription_tree_destructor(void *obj)
{ {
struct sip_subscription_tree *sub_tree = obj; struct sip_subscription_tree *sub_tree = obj;
ast_debug(3, "Destroying subscription tree %p\n", sub_tree);
remove_subscription(sub_tree); remove_subscription(sub_tree);
subscription_persistence_remove(sub_tree); subscription_persistence_remove(sub_tree);
@ -1189,14 +1207,18 @@ static void subscription_tree_destructor(void *obj)
if (sub_tree->dlg) { if (sub_tree->dlg) {
ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree); ast_sip_push_task_synchronous(NULL, subscription_remove_serializer, sub_tree);
} }
destroy_subscriptions(sub_tree->root);
shutdown_subscriptions(sub_tree->root);
ao2_cleanup(sub_tree->root);
ast_taskprocessor_unreference(sub_tree->serializer); ast_taskprocessor_unreference(sub_tree->serializer);
ast_module_unref(ast_module_info->self); ast_module_unref(ast_module_info->self);
} }
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
{
ast_debug(3, "Removing subscription %p reference to subscription tree %p\n", sub, sub->tree);
ao2_cleanup(sub->tree);
}
static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg) static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
{ {
/* We keep a reference to the dialog until our subscription is destroyed. See /* We keep a reference to the dialog until our subscription is destroyed. See
@ -1654,6 +1676,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
#ifdef TEST_FRAMEWORK #ifdef TEST_FRAMEWORK
struct ast_sip_endpoint *endpoint = sub_tree->endpoint; struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
#endif #endif
pjsip_evsub *evsub = sub_tree->evsub;
int res; int res;
if (allocate_tdata_buffer(tdata)) { if (allocate_tdata_buffer(tdata)) {
@ -1661,13 +1684,13 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
return -1; return -1;
} }
res = pjsip_evsub_send_request(sub_tree->evsub, tdata) == PJ_SUCCESS ? 0 : -1; res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
subscription_persistence_update(sub_tree, NULL); subscription_persistence_update(sub_tree, NULL);
ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET", ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
"StateText: %s\r\n" "StateText: %s\r\n"
"Endpoint: %s\r\n", "Endpoint: %s\r\n",
pjsip_evsub_get_state_name(sub_tree->evsub), pjsip_evsub_get_state_name(evsub),
ast_sorcery_object_get_id(endpoint)); ast_sorcery_object_get_id(endpoint));
return res; return res;
@ -2075,6 +2098,8 @@ static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
/*! /*!
* \brief Send a NOTIFY request to a subscriber * \brief Send a NOTIFY request to a subscriber
* *
* \pre sub_tree->dlg is locked
*
* \param sub_tree The subscription tree representing the subscription * \param sub_tree The subscription tree representing the subscription
* \param force_full_state If true, ignore resource list settings and send full resource list state. * \param force_full_state If true, ignore resource list settings and send full resource list state.
* \retval 0 Success * \retval 0 Success
@ -2107,6 +2132,9 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
} }
if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
sub_tree->last_notify = 1;
}
if (sip_subscription_send_request(sub_tree, tdata)) { if (sip_subscription_send_request(sub_tree, tdata)) {
return -1; return -1;
} }
@ -2120,12 +2148,14 @@ static int serialized_send_notify(void *userdata)
{ {
struct sip_subscription_tree *sub_tree = userdata; struct sip_subscription_tree *sub_tree = userdata;
pjsip_dlg_inc_lock(sub_tree->dlg);
/* It's possible that between when the notification was scheduled /* It's possible that between when the notification was scheduled
* and now, that a new SUBSCRIBE arrived, requiring full state to be * and now, that a new SUBSCRIBE arrived, requiring full state to be
* sent out in an immediate NOTIFY. If that has happened, we need to * sent out in an immediate NOTIFY. If that has happened, we need to
* bail out here instead of sending the batched NOTIFY. * bail out here instead of sending the batched NOTIFY.
*/ */
if (!sub_tree->send_scheduled_notify) { if (!sub_tree->send_scheduled_notify) {
pjsip_dlg_dec_lock(sub_tree->dlg);
ao2_cleanup(sub_tree); ao2_cleanup(sub_tree);
return 0; return 0;
} }
@ -2135,6 +2165,7 @@ static int serialized_send_notify(void *userdata)
"Resource: %s", "Resource: %s",
sub_tree->root->resource); sub_tree->root->resource);
sub_tree->notify_sched_id = -1; sub_tree->notify_sched_id = -1;
pjsip_dlg_dec_lock(sub_tree->dlg);
ao2_cleanup(sub_tree); ao2_cleanup(sub_tree);
return 0; return 0;
} }
@ -2167,8 +2198,18 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data, int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data,
int terminate) int terminate)
{ {
int res;
pjsip_dlg_inc_lock(sub->tree->dlg);
if (!sub->tree->evsub) {
pjsip_dlg_dec_lock(sub->tree->dlg);
return 0;
}
if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub), if (ast_sip_pubsub_generate_body_content(ast_sip_subscription_get_body_type(sub),
ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) { ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) {
pjsip_dlg_dec_lock(sub->tree->dlg);
return -1; return -1;
} }
@ -2178,9 +2219,8 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
} }
if (sub->tree->notification_batch_interval) { if (sub->tree->notification_batch_interval) {
return schedule_notification(sub->tree); res = schedule_notification(sub->tree);
} else { } else {
int res;
/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */ /* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
ao2_ref(sub->tree, +1); ao2_ref(sub->tree, +1);
res = send_notify(sub->tree, 0); res = send_notify(sub->tree, 0);
@ -2188,9 +2228,10 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
"Resource: %s", "Resource: %s",
sub->tree->root->resource); sub->tree->root->resource);
ao2_ref(sub->tree, -1); ao2_ref(sub->tree, -1);
return res;
} }
pjsip_dlg_dec_lock(sub->tree->dlg);
return res;
} }
void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size) void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
@ -3139,24 +3180,6 @@ static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
return PJ_FALSE; return PJ_FALSE;
} }
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
{
struct sip_subscription_tree *sub_tree;
if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
return;
}
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
if (!sub_tree) {
return;
}
ao2_cleanup(sub_tree);
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
}
static void set_state_terminated(struct ast_sip_subscription *sub) static void set_state_terminated(struct ast_sip_subscription *sub)
{ {
int i; int i;
@ -3167,6 +3190,138 @@ static void set_state_terminated(struct ast_sip_subscription *sub)
} }
} }
/* XXX This function and serialized_pubsub_on_rx_refresh are nearly identical */
static int serialized_pubsub_on_server_timeout(void *userdata)
{
struct sip_subscription_tree *sub_tree = userdata;
pjsip_dlg_inc_lock(sub_tree->dlg);
if (!sub_tree->evsub) {
pjsip_dlg_dec_lock(sub_tree->dlg);
return 0;
}
set_state_terminated(sub_tree->root);
send_notify(sub_tree, 1);
ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
"Resource: %s",
sub_tree->root->resource);
pjsip_dlg_dec_lock(sub_tree->dlg);
ao2_cleanup(sub_tree);
return 0;
}
/*!
* \brief PJSIP callback when underlying SIP subscription changes state
*
* This callback is a bit of a mess, because it's not always called when
* you might expect it to be, and it can be called multiple times for the
* same state.
*
* For instance, this function is not called at all when an incoming SUBSCRIBE
* arrives to refresh a subscription. That makes sense in a way, since the
* subscription state has not made a change; it was active and remains active.
*
* However, if an incoming SUBSCRIBE arrives to end a subscription, then this
* will be called into once upon receiving the SUBSCRIBE (after the call to
* pubsub_on_rx_refresh) and again when sending a NOTIFY to end the subscription.
* In both cases, the apparent state of the subscription is "terminated".
*
* However, the double-terminated state changes don't happen in all cases. For
* instance, if a subscription expires, then the only time this callback is
* called is when we send the NOTIFY to end the subscription.
*
* As far as state changes are concerned, we only ever care about transitions
* to the "terminated" state. The action we take here is dependent on the
* conditions behind why the state change to "terminated" occurred. If the
* state change has occurred because we are sending a NOTIFY to end the
* subscription, we consider this to be the final hurrah of the subscription
* and take measures to start shutting things down. If the state change to
* terminated occurs for a different reason (e.g. transaction timeout,
* incoming SUBSCRIBE to end the subscription), then we push a task to
* send out a NOTIFY. When that NOTIFY is sent, this callback will be
* called again and we will actually shut down the subscription. The
* subscription tree's last_notify field let's us know if this is being
* called as a result of a terminating NOTIFY or not.
*
* There is no guarantee that this function will be called from a serializer
* thread since it can be called due to a transaction timeout. Therefore
* synchronization primitives are necessary to ensure that no operations
* step on each others' toes. The dialog lock is always held when this
* callback is called, so we ensure that relevant structures that may
* be touched in this function are always protected by the dialog lock
* elsewhere as well. The dialog lock in particular protects
*
* \li The subscription tree's last_notify field
* \li The subscription tree's evsub pointer
*/
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
{
struct sip_subscription_tree *sub_tree;
ast_debug(3, "on_evsub_state called with state %s\n", pjsip_evsub_get_state_name(evsub));
if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
return;
}
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
if (!sub_tree) {
return;
}
if (!sub_tree->last_notify) {
if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, ao2_bump(sub_tree))) {
ast_log(LOG_ERROR, "Failed to push task to send final NOTIFY.\n");
ao2_ref(sub_tree, -1);
} else {
return;
}
}
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
sub_tree->evsub = NULL;
shutdown_subscriptions(sub_tree->root);
/* Remove evsub's reference to the sub_tree */
ao2_ref(sub_tree, -1);
}
static int serialized_pubsub_on_rx_refresh(void *userdata)
{
struct sip_subscription_tree *sub_tree = userdata;
pjsip_dlg_inc_lock(sub_tree->dlg);
if (!sub_tree->evsub) {
pjsip_dlg_dec_lock(sub_tree->dlg);
return 0;
}
if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
set_state_terminated(sub_tree->root);
}
send_notify(sub_tree, 1);
ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
"Resource: %s", sub_tree->root->resource);
pjsip_dlg_dec_lock(sub_tree->dlg);
ao2_cleanup(sub_tree);
return 0;
}
/*!
* \brief Called whenever an in-dialog SUBSCRIBE is received
*
* This includes both SUBSCRIBE requests that actually refresh the subscription
* as well as SUBSCRIBE requests that end the subscription.
*
* In the case where the SUBSCRIBE is actually refreshing the subscription we
* push a task to send an appropriate NOTIFY request. In the case where the
* SUBSCRIBE is ending the subscription, we let the pubsub_on_evsub_state
* callback take care of sending the terminal NOTIFY request instead.
*/
static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata, static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
{ {
@ -3177,31 +3332,19 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
return; return;
} }
/* If sending a NOTIFY to terminate a subscription, then pubsub_on_evsub_state() /* PJSIP will set the evsub's state to terminated before calling into this function
* will be called when we send the NOTIFY, and that will result in dropping the * if the Expires value of the incoming SUBSCRIBE is 0.
* refcount of sub_tree by one, and possibly destroying the sub_tree. We need to
* hold a reference to the sub_tree until this function returns so that we don't
* try to read from or write to freed memory by accident
*/ */
ao2_ref(sub_tree, +1); if (pjsip_evsub_get_state(sub_tree->evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_rx_refresh, ao2_bump(sub_tree))) {
if (pjsip_evsub_get_state(evsub) == PJSIP_EVSUB_STATE_TERMINATED) { /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
set_state_terminated(sub_tree->root); ao2_ref(sub_tree, -1);
}
} }
if (send_notify(sub_tree, 1)) {
*p_st_code = 500;
}
ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
"SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
"Resource: %s", sub_tree->root->resource);
if (sub_tree->is_list) { if (sub_tree->is_list) {
pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool)); pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
} }
ao2_ref(sub_tree, -1);
} }
static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code, static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
@ -3239,31 +3382,24 @@ static void pubsub_on_client_refresh(pjsip_evsub *evsub)
ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree); ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
} }
static int serialized_pubsub_on_server_timeout(void *userdata)
{
struct sip_subscription_tree *sub_tree = userdata;
set_state_terminated(sub_tree->root);
send_notify(sub_tree, 1);
ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
"Resource: %s",
sub_tree->root->resource);
ao2_cleanup(sub_tree);
return 0;
}
static void pubsub_on_server_timeout(pjsip_evsub *evsub) static void pubsub_on_server_timeout(pjsip_evsub *evsub)
{ {
struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
if (!sub_tree) { if (!sub_tree) {
/* if a subscription has been terminated and the subscription /* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
timeout/expires is less than the time it takes for all pending * with Expires: 0 arrives to end a subscription, nor does it terminate
transactions to end then the subscription timer will not have * this timer when we send a NOTIFY request in response to receiving such
been canceled yet and sub will be null, so do nothing since * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
the subscription has already been terminated. */ * NOTIFY transaction has finished (either through receiving a response
return; * or through a transaction timeout).
*
* Therefore, it is possible that we can be told that a server timeout
* occurred after we already thought that the subscription had been
* terminated. In such a case, we will have already removed the sub_tree
* from the evsub's mod_data array.
*/
return;
} }
ao2_ref(sub_tree, +1); ao2_ref(sub_tree, +1);

View File

@ -38,6 +38,7 @@
LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri; LINKER_SYMBOL_PREFIXast_sip_subscription_get_remote_uri;
LINKER_SYMBOL_PREFIXast_sip_subscription_get_header; LINKER_SYMBOL_PREFIXast_sip_subscription_get_header;
LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated; LINKER_SYMBOL_PREFIXast_sip_subscription_is_terminated;
LINKER_SYMBOL_PREFIXast_sip_subscription_destroy;
local: local:
*; *;
}; };