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: https://svn.pjsip.org/repos/pjproject/trunk@2956 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2009-10-20 13:56:26 +00:00
parent 79f4f20268
commit 73bb72384f
6 changed files with 374 additions and 123 deletions

View File

@ -2619,12 +2619,16 @@ static void on_buddy_state(pjsua_buddy_id buddy_id)
pjsua_buddy_info info;
pjsua_buddy_get_info(buddy_id, &info);
PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s (subscription state is %s)",
PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
"(last termination reason code=%d %.*s)",
(int)info.uri.slen,
info.uri.ptr,
(int)info.status_text.slen,
info.status_text.ptr,
info.sub_state_name));
info.sub_state_name,
info.sub_term_code,
(int)info.sub_term_reason.slen,
info.sub_term_reason.ptr));
}

View File

@ -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>

View File

@ -1396,6 +1396,32 @@ PJ_DECL(pj_status_t) pjsua_cancel_stun_resolution(void *token,
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;

View File

@ -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;

View File

@ -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

View File

@ -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;
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();
status = lock_buddy("pjsua_buddy_subscribe_pres()", buddy_id, &lck, 0);
if (status != PJ_SUCCESS)
return status;
buddy = &pjsua_var.buddy[buddy_id];
buddy->monitor = subscribe;
lck.buddy->monitor = subscribe;
PJSUA_UNLOCK();
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();
status = lock_buddy("pjsua_buddy_update_pres()", buddy_id, &lck, 0);
if (status != PJ_SUCCESS)
return status;
buddy = &pjsua_var.buddy[buddy_id];
/* 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;
@ -1706,22 +1910,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.
*/
@ -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);
}
}