summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-10-20 13:56:26 +0000
committerBenny Prijono <bennylp@teluu.com>2009-10-20 13:56:26 +0000
commit89547e9f9d2eff5aa4dd7bbd212ebd7cee8b4e96 (patch)
tree521151a3f1724381ebf27dffd0f24ba251bc76af /pjsip
parentc1e884baaed1d9990ede66182d4d535c7b855779 (diff)
Initial commit for ticket #937: Revamping of presence management to make it more efficient
Presence enhancements: - finer grained buddy lock object, instead of using global PJSUA-LIB's mutex - individual resubscription timer for buddies and also add random delay interval so that resubscriptions don't happen simultaneously (may hog processing and bandwidth). - in general reduced the use of global PJSUA-LIB's mutex for more efficiency - added last termination code in buddy info - use the RPID note's text for buddy's offline status rather than the default "offline" status, if available - resubscribe automatically on several termination causes as explained in the ticket (still untested) General enhancements: - added pjsua_schedule_timer() and pjsua_cancel_timer() APIs git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2956 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip_simple.h1
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h37
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h3
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c17
-rw-r--r--pjsip/src/pjsua-lib/pjsua_pres.c431
5 files changed, 368 insertions, 121 deletions
diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h
index ba368341..8fda8152 100644
--- a/pjsip/include/pjsip_simple.h
+++ b/pjsip/include/pjsip_simple.h
@@ -35,6 +35,7 @@
#define __PJSIP_SIMPLE_H__
#include <pjsip-simple/evsub.h>
+#include <pjsip-simple/evsub_msg.h>
#include <pjsip-simple/iscomposing.h>
#include <pjsip-simple/presence.h>
#include <pjsip-simple/pidf.h>
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index c0eb8b25..93167785 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1397,6 +1397,32 @@ PJ_DECL(pj_status_t) pjsua_verify_sip_url(const char *url);
/**
+ * Schedule a timer entry. Note that the timer callback may be executed
+ * by different thread, depending on whether worker thread is enabled or
+ * not.
+ *
+ * @param entry Timer heap entry.
+ * @param delay The interval to expire.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ *
+ * @see pjsip_endpt_schedule_timer()
+ */
+PJ_DECL(pj_status_t) pjsua_schedule_timer(pj_timer_entry *entry,
+ const pj_time_val *delay);
+
+
+/**
+ * Cancel the previously scheduled timer.
+ *
+ * @param entry Timer heap entry.
+ *
+ * @see pjsip_endpt_cancel_timer()
+ */
+PJ_DECL(void) pjsua_cancel_timer(pj_timer_entry *entry);
+
+
+/**
* This is a utility function to display error message for the specified
* error code. The error message will be sent to the log.
*
@@ -3128,7 +3154,16 @@ typedef struct pjsua_buddy_info
const char *sub_state_name;
/**
- * Specifies the last presence subscription terminatino reason. If
+ * Specifies the last presence subscription termination code. This would
+ * return the last status of the SUBSCRIBE request. If the subscription
+ * is terminated with NOTIFY by the server, this value will be set to
+ * 200, and subscription termination reason will be given in the
+ * \a sub_term_reason field.
+ */
+ unsigned sub_term_code;
+
+ /**
+ * Specifies the last presence subscription termination reason. If
* presence subscription is currently active, the value will be empty.
*/
pj_str_t sub_term_reason;
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index e4691868..0f777967 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -185,9 +185,10 @@ typedef struct pjsua_buddy
pj_bool_t monitor; /**< Should we monitor? */
pjsip_dialog *dlg; /**< The underlying dialog. */
pjsip_evsub *sub; /**< Buddy presence subscription */
+ unsigned term_code; /**< Subscription termination code */
pj_str_t term_reason;/**< Subscription termination reason */
pjsip_pres_status status; /**< Buddy presence status. */
-
+ pj_timer_entry timer; /**< Resubscription timer */
} pjsua_buddy;
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index f5e0fba4..773785ed 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -2318,6 +2318,23 @@ PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
return p ? 0 : -1;
}
+/*
+ * Schedule a timer entry.
+ */
+PJ_DEF(pj_status_t) pjsua_schedule_timer( pj_timer_entry *entry,
+ const pj_time_val *delay)
+{
+ return pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, delay);
+}
+
+/*
+ * Cancel the previously scheduled timer.
+ *
+ */
+PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry)
+{
+ pjsip_endpt_cancel_timer(pjsua_var.endpt, entry);
+}
/**
* Normalize route URI (check for ";lr" and append one if it doesn't
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index 935a41e7..88a0f66b 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -24,13 +24,14 @@
#define THIS_FILE "pjsua_pres.c"
-static void subscribe_buddy_presence(unsigned index);
+static void subscribe_buddy_presence(pjsua_buddy_id buddy_id);
+static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id);
/*
* Find buddy.
*/
-static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
+static pjsua_buddy_id find_buddy(const pjsip_uri *uri)
{
const pjsip_sip_uri *sip_uri;
unsigned i;
@@ -60,6 +61,87 @@ static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
return PJSUA_INVALID_ID;
}
+#define LOCK_DIALOG 1
+#define LOCK_PJSUA 2
+#define LOCK_ALL (LOCK_DIALOG | LOCK_PJSUA)
+
+/* Buddy lock object */
+struct buddy_lock
+{
+ pjsua_buddy *buddy;
+ pjsip_dialog *dlg;
+ pj_uint8_t flag;
+};
+
+/* Acquire lock to the specified buddy_id */
+pj_status_t lock_buddy(const char *title,
+ pjsua_buddy_id buddy_id,
+ struct buddy_lock *lck,
+ unsigned _unused_)
+{
+ enum { MAX_RETRY=50 };
+ pj_bool_t has_pjsua_lock = PJ_FALSE;
+ unsigned retry;
+
+ PJ_UNUSED_ARG(_unused_);
+
+ pj_bzero(lck, sizeof(*lck));
+
+ for (retry=0; retry<MAX_RETRY; ++retry) {
+
+ if (PJSUA_TRY_LOCK() != PJ_SUCCESS) {
+ pj_thread_sleep(retry/10);
+ continue;
+ }
+
+ has_pjsua_lock = PJ_TRUE;
+ lck->flag = LOCK_PJSUA;
+ lck->buddy = &pjsua_var.buddy[buddy_id];
+
+ if (lck->buddy->dlg == NULL)
+ return PJ_SUCCESS;
+
+ if (pjsip_dlg_try_inc_lock(lck->buddy->dlg) != PJ_SUCCESS) {
+ lck->flag = 0;
+ lck->buddy = NULL;
+ has_pjsua_lock = PJ_FALSE;
+ PJSUA_UNLOCK();
+ pj_thread_sleep(retry/10);
+ continue;
+ }
+
+ lck->dlg = lck->buddy->dlg;
+ lck->flag = LOCK_DIALOG;
+ PJSUA_UNLOCK();
+
+ break;
+ }
+
+ if (lck->flag == 0) {
+ if (has_pjsua_lock == PJ_FALSE)
+ PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
+ "(possibly system has deadlocked) in %s",
+ title));
+ else
+ PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
+ "(possibly system has deadlocked) in %s",
+ title));
+ return PJ_ETIMEDOUT;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Release buddy lock */
+static void unlock_buddy(struct buddy_lock *lck)
+{
+ if (lck->flag & LOCK_DIALOG)
+ pjsip_dlg_dec_lock(lck->dlg);
+
+ if (lck->flag & LOCK_PJSUA)
+ PJSUA_UNLOCK();
+}
+
/*
* Get total number of buddies.
@@ -86,8 +168,11 @@ PJ_DEF(pjsua_buddy_id) pjsua_buddy_find(const pj_str_t *uri_str)
uri = pjsip_parse_uri(pool, input.ptr, input.slen, 0);
if (!uri)
buddy_id = PJSUA_INVALID_ID;
- else
- buddy_id = pjsua_find_buddy(uri);
+ else {
+ PJSUA_LOCK();
+ buddy_id = find_buddy(uri);
+ PJSUA_UNLOCK();
+ }
pj_pool_release(pool);
@@ -139,20 +224,22 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
pjsua_buddy_info *info)
{
unsigned total=0;
+ struct buddy_lock lck;
pjsua_buddy *buddy;
+ pj_status_t status;
- PJ_ASSERT_RETURN(buddy_id>=0 &&
- buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
- PJ_EINVAL);
-
- PJSUA_LOCK();
+ PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
pj_bzero(info, sizeof(pjsua_buddy_info));
- buddy = &pjsua_var.buddy[buddy_id];
+ status = lock_buddy("pjsua_buddy_get_info()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ buddy = lck.buddy;
info->id = buddy->index;
if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -186,13 +273,19 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
} else {
info->status = PJSUA_BUDDY_STATUS_OFFLINE;
- info->status_text = pj_str("Offline");
+ info->rpid = buddy->status.info[0].rpid;
+
+ if (info->rpid.note.slen)
+ info->status_text = info->rpid.note;
+ else
+ info->status_text = pj_str("Offline");
}
/* monitor pres */
info->monitor_pres = buddy->monitor;
/* subscription state and termination reason */
+ info->sub_term_code = buddy->term_code;
if (buddy->sub) {
info->sub_state = pjsip_evsub_get_state(buddy->sub);
info->sub_state_name = pjsip_evsub_get_state_name(buddy->sub);
@@ -218,7 +311,7 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
info->sub_term_reason = pj_str("");
}
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -228,15 +321,18 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
void *user_data)
{
- PJ_ASSERT_RETURN(buddy_id>=0 &&
- buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
- PJ_EINVAL);
+ struct buddy_lock lck;
+ pj_status_t status;
- PJSUA_LOCK();
+ PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
+
+ status = lock_buddy("pjsua_buddy_set_user_data()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
pjsua_var.buddy[buddy_id].user_data = user_data;
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -247,17 +343,19 @@ PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
*/
PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
{
+ struct buddy_lock lck;
+ pj_status_t status;
void *user_data;
- PJ_ASSERT_RETURN(buddy_id>=0 &&
- buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
- NULL);
+ PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), NULL);
- PJSUA_LOCK();
+ status = lock_buddy("pjsua_buddy_get_user_data()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return NULL;
user_data = pjsua_var.buddy[buddy_id].user_data;
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return user_data;
}
@@ -382,6 +480,9 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
*/
PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
{
+ struct buddy_lock lck;
+ pj_status_t status;
+
PJ_ASSERT_RETURN(buddy_id>=0 &&
buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
PJ_EINVAL);
@@ -390,11 +491,13 @@ PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
return PJ_SUCCESS;
}
+ status = lock_buddy("pjsua_buddy_del()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
/* Unsubscribe presence */
pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
- PJSUA_LOCK();
-
/* Not interested with further events for this buddy */
if (pjsua_var.buddy[buddy_id].sub) {
pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub,
@@ -408,7 +511,7 @@ PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
/* Reset buddy struct */
reset_buddy(buddy_id);
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -419,21 +522,20 @@ PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
pj_bool_t subscribe)
{
- pjsua_buddy *buddy;
-
- PJ_ASSERT_RETURN(buddy_id>=0 &&
- buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
- PJ_EINVAL);
+ struct buddy_lock lck;
+ pj_status_t status;
- PJSUA_LOCK();
+ PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
- buddy = &pjsua_var.buddy[buddy_id];
- buddy->monitor = subscribe;
+ status = lock_buddy("pjsua_buddy_subscribe_pres()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
- PJSUA_UNLOCK();
+ lck.buddy->monitor = subscribe;
- pjsua_pres_refresh();
+ pjsua_buddy_update_pres(buddy_id);
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -443,32 +545,32 @@ PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
*/
PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
{
- pjsua_buddy *buddy;
+ struct buddy_lock lck;
+ pj_status_t status;
- PJ_ASSERT_RETURN(buddy_id>=0 &&
- buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
- PJ_EINVAL);
+ PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
- PJSUA_LOCK();
-
- buddy = &pjsua_var.buddy[buddy_id];
+ status = lock_buddy("pjsua_buddy_update_pres()", buddy_id, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
- /* Return error if buddy's presence monitoring is not enabled */
- if (!buddy->monitor) {
- PJSUA_UNLOCK();
- return PJ_EINVALIDOP;
+ /* Is this an unsubscribe request? */
+ if (!lck.buddy->monitor) {
+ unsubscribe_buddy_presence(buddy_id);
+ unlock_buddy(&lck);
+ return PJ_SUCCESS;
}
/* Ignore if presence is already active for the buddy */
- if (buddy->sub) {
- PJSUA_UNLOCK();
+ if (lck.buddy->sub) {
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
/* Initiate presence subscription */
subscribe_buddy_presence(buddy_id);
- PJSUA_UNLOCK();
+ unlock_buddy(&lck);
return PJ_SUCCESS;
}
@@ -805,7 +907,7 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
pjsua_buddy_id buddy_id;
- buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
+ buddy_id = find_buddy(rdata->msg_info.from->uri);
(*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
&dlg->remote.info_str,
@@ -951,7 +1053,7 @@ PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
/* Subscribe to buddy's presence if we're not subscribed */
- buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
+ buddy_id = find_buddy(srv_pres->dlg->remote.info->uri);
if (buddy_id != PJSUA_INVALID_ID) {
pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
if (b->monitor && b->sub == NULL) {
@@ -1280,6 +1382,40 @@ void pjsua_pres_update_acc(int acc_id, pj_bool_t force)
* Client subscription.
*/
+static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
+{
+ pjsua_buddy *buddy = (pjsua_buddy*)entry->user_data;
+
+ PJ_UNUSED_ARG(th);
+
+ entry->id = PJ_FALSE;
+ pjsua_buddy_update_pres(buddy->index);
+}
+
+/* Reschedule subscription refresh timer or terminate the subscription
+ * refresh timer for the specified buddy.
+ */
+static void buddy_resubscribe(pjsua_buddy *buddy, pj_bool_t resched,
+ unsigned msec_interval)
+{
+ if (buddy->timer.id) {
+ pjsua_cancel_timer(&buddy->timer);
+ buddy->timer.id = PJ_FALSE;
+ }
+
+ if (resched) {
+ pj_time_val delay;
+
+ pj_timer_entry_init(&buddy->timer, 0, buddy, &buddy_timer_cb);
+ delay.sec = 0;
+ delay.msec = msec_interval;
+ pj_time_val_normalize(&delay);
+
+ if (pjsua_schedule_timer(&buddy->timer, &delay)==PJ_SUCCESS)
+ buddy->timer.id = PJ_TRUE;
+ }
+}
+
/* Callback called when *client* subscription state has changed. */
static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
{
@@ -1287,8 +1423,10 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
PJ_UNUSED_ARG(event);
- PJSUA_LOCK();
-
+ /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+ * a dialog attached to it, lock_buddy() will use the dialog
+ * lock, which we are currently holding!
+ */
buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (buddy) {
PJ_LOG(4,(THIS_FILE,
@@ -1298,6 +1436,8 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
pjsip_evsub_get_state_name(sub)));
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ int resub_delay = -1;
+
if (buddy->term_reason.ptr == NULL) {
buddy->term_reason.ptr = (char*)
pj_pool_alloc(buddy->pool,
@@ -1306,7 +1446,87 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
pj_strncpy(&buddy->term_reason,
pjsip_evsub_get_termination_reason(sub),
PJSUA_BUDDY_SUB_TERM_REASON_LEN);
+
+ buddy->term_code = 200;
+
+ /* Determine whether to resubscribe automatically */
+ if (event->type==PJSIP_EVENT_TSX_STATE) {
+ const pjsip_transaction *tsx = event->body.tsx_state.tsx;
+ if (pjsip_method_cmp(&tsx->method,
+ &pjsip_subscribe_method)==0)
+ {
+ buddy->term_code = tsx->status_code;
+ switch (tsx->status_code) {
+ case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
+ /* 481: we refreshed too late? resubscribe
+ * immediately.
+ */
+ resub_delay = 500;
+ break;
+ }
+ } else if (pjsip_method_cmp(&tsx->method,
+ &pjsip_notify_method)==0)
+ {
+ if (pj_stricmp2(&buddy->term_reason, "deactivated")==0 ||
+ pj_stricmp2(&buddy->term_reason, "timeout")==0) {
+ /* deactivated: The subscription has been terminated,
+ * but the subscriber SHOULD retry immediately with
+ * a new subscription.
+ */
+ /* timeout: The subscription has been terminated
+ * because it was not refreshed before it expired.
+ * Clients MAY re-subscribe immediately. The
+ * "retry-after" parameter has no semantics for
+ * "timeout".
+ */
+ resub_delay = 500;
+ }
+ else if (pj_stricmp2(&buddy->term_reason, "probation")==0||
+ pj_stricmp2(&buddy->term_reason, "giveup")==0) {
+ /* probation: The subscription has been terminated,
+ * but the client SHOULD retry at some later time.
+ * If a "retry-after" parameter is also present, the
+ * client SHOULD wait at least the number of seconds
+ * specified by that parameter before attempting to re-
+ * subscribe.
+ */
+ /* giveup: The subscription has been terminated because
+ * the notifier could not obtain authorization in a
+ * timely fashion. If a "retry-after" parameter is
+ * also present, the client SHOULD wait at least the
+ * number of seconds specified by that parameter before
+ * attempting to re-subscribe; otherwise, the client
+ * MAY retry immediately, but will likely get put back
+ * into pending state.
+ */
+ const pjsip_sub_state_hdr *sub_hdr;
+ pj_str_t sub_state = { "Subscription-State", 18 };
+ const pjsip_msg *msg;
+
+ msg = event->body.tsx_state.src.rdata->msg_info.msg;
+ sub_hdr = (const pjsip_sub_state_hdr*)
+ pjsip_msg_find_hdr_by_name(msg, &sub_state,
+ NULL);
+ if (sub_hdr && sub_hdr->retry_after > 0)
+ resub_delay = sub_hdr->retry_after * 1000;
+ }
+
+ }
+ }
+
+ /* For other cases of subscription termination, if resubscribe
+ * timer is not set, schedule with default expiration (plus minus
+ * some random value, to avoid sending SUBSCRIBEs all at once)
+ */
+ if (resub_delay == -1) {
+ pj_assert(PJSUA_PRES_TIMER >= 3);
+ resub_delay = PJSUA_PRES_TIMER*1000 - 2500 + (pj_rand()%5000);
+ }
+
+ buddy_resubscribe(buddy, PJ_TRUE, resub_delay);
+
} else {
+ buddy->term_code = 0;
buddy->term_reason.slen = 0;
}
@@ -1318,11 +1538,10 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
buddy->sub = NULL;
buddy->status.info_cnt = 0;
+ buddy->dlg = NULL;
pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
}
}
-
- PJSUA_UNLOCK();
}
@@ -1334,11 +1553,12 @@ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
pjsua_buddy *buddy;
pjsip_contact_hdr *contact_hdr;
- PJSUA_LOCK();
-
+ /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+ * a dialog attached to it, lock_buddy() will use the dialog
+ * lock, which we are currently holding!
+ */
buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (!buddy) {
- PJSUA_UNLOCK();
return;
}
@@ -1347,7 +1567,6 @@ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
*/
if (buddy->contact.slen != 0) {
/* Contact already set */
- PJSUA_UNLOCK();
return;
}
@@ -1357,7 +1576,6 @@ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
event->type != PJSIP_EVENT_RX_MSG ||
pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
{
- PJSUA_UNLOCK();
return;
}
@@ -1366,7 +1584,6 @@ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
PJSIP_H_CONTACT, NULL);
if (!contact_hdr) {
- PJSUA_UNLOCK();
return;
}
@@ -1378,8 +1595,6 @@ static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub,
PJSIP_MAX_URL_SIZE);
if (buddy->contact.slen < 0)
buddy->contact.slen = 0;
-
- PJSUA_UNLOCK();
}
@@ -1393,8 +1608,10 @@ static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
{
pjsua_buddy *buddy;
- PJSUA_LOCK();
-
+ /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+ * a dialog attached to it, lock_buddy() will use the dialog
+ * lock, which we are currently holding!
+ */
buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (buddy) {
/* Update our info. */
@@ -1409,8 +1626,6 @@ static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
PJ_UNUSED_ARG(p_st_text);
PJ_UNUSED_ARG(res_hdr);
PJ_UNUSED_ARG(p_body);
-
- PJSUA_UNLOCK();
}
@@ -1436,7 +1651,7 @@ static pjsip_evsub_user pres_callback =
/* It does what it says.. */
-static void subscribe_buddy_presence(unsigned index)
+static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
{
pj_pool_t *tmp_pool = NULL;
pjsua_buddy *buddy;
@@ -1446,13 +1661,13 @@ static void subscribe_buddy_presence(unsigned index)
pjsip_tx_data *tdata;
pj_status_t status;
- buddy = &pjsua_var.buddy[index];
+ buddy = &pjsua_var.buddy[buddy_id];
acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
acc = &pjsua_var.acc[acc_id];
PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
- acc_id, index));
+ acc_id, buddy_id));
/* Generate suitable Contact header unless one is already set in
* the account
@@ -1493,7 +1708,7 @@ static void subscribe_buddy_presence(unsigned index)
status = pjsip_pres_create_uac( buddy->dlg, &pres_callback,
PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
if (status != PJ_SUCCESS) {
- pjsua_var.buddy[index].sub = NULL;
+ buddy->sub = NULL;
pjsua_perror(THIS_FILE, "Unable to create presence client",
status);
/* This should destroy the dialog since there's no session
@@ -1564,19 +1779,19 @@ static void subscribe_buddy_presence(unsigned index)
/* It does what it says... */
-static void unsubscribe_buddy_presence(unsigned index)
+static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id)
{
pjsua_buddy *buddy;
pjsip_tx_data *tdata;
pj_status_t status;
- buddy = &pjsua_var.buddy[index];
+ buddy = &pjsua_var.buddy[buddy_id];
if (buddy->sub == NULL)
return;
if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
- pjsua_var.buddy[index].sub = NULL;
+ buddy->sub = NULL;
return;
}
@@ -1594,39 +1809,22 @@ static void unsubscribe_buddy_presence(unsigned index)
}
}
-
-/* Lock all buddies */
-#define LOCK_BUDDIES unsigned cnt_ = 0; \
- pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
- unsigned i_; \
- for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
- if (pjsua_var.buddy[i_].sub) { \
- dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
- pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
- } \
- } \
- PJSUA_LOCK();
-
-/* Unlock all buddies */
-#define UNLOCK_BUDDIES PJSUA_UNLOCK(); \
- for (i_=0; i_<cnt_; ++i_) { \
- pjsip_dlg_dec_lock(dlg_list_[i_]); \
- }
-
-
-
/* It does what it says.. */
-static void refresh_client_subscriptions(void)
+static pj_status_t refresh_client_subscriptions(void)
{
unsigned i;
-
- LOCK_BUDDIES;
+ pj_status_t status;
for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
+ struct buddy_lock lck;
- if (!pjsua_var.buddy[i].uri.slen)
+ if (!pjsua_buddy_is_valid(i))
continue;
+ status = lock_buddy("refresh_client_subscriptions()", i, &lck, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
subscribe_buddy_presence(i);
@@ -1634,9 +1832,11 @@ static void refresh_client_subscriptions(void)
unsubscribe_buddy_presence(i);
}
+
+ unlock_buddy(&lck);
}
- UNLOCK_BUDDIES;
+ return PJ_SUCCESS;
}
/* Timer callback to re-create client subscription */
@@ -1654,7 +1854,11 @@ static void pres_timer_cb(pj_timer_heap_t *th,
}
entry->id = PJ_FALSE;
- refresh_client_subscriptions();
+
+ /* #937: No need to do bulk client refresh, as buddies have their
+ * own individual timer now.
+ */
+ //refresh_client_subscriptions();
pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
entry->id = PJ_TRUE;
@@ -1707,22 +1911,6 @@ pj_status_t pjsua_pres_start(void)
/*
- * Refresh presence subscriptions
- */
-void pjsua_pres_refresh()
-{
- unsigned i;
-
- refresh_client_subscriptions();
-
- for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
- if (pjsua_var.acc[i].valid)
- pjsua_pres_update_acc(i, PJ_FALSE);
- }
-}
-
-
-/*
* Shutdown presence.
*/
void pjsua_pres_shutdown(void)
@@ -1746,5 +1934,10 @@ void pjsua_pres_shutdown(void)
pjsua_var.buddy[i].monitor = 0;
}
- pjsua_pres_refresh();
+ refresh_client_subscriptions();
+
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+ if (pjsua_var.acc[i].valid)
+ pjsua_pres_update_acc(i, PJ_FALSE);
+ }
}