From 89547e9f9d2eff5aa4dd7bbd212ebd7cee8b4e96 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 20 Oct 2009 13:56:26 +0000 Subject: 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 --- pjsip/include/pjsip_simple.h | 1 + pjsip/include/pjsua-lib/pjsua.h | 37 ++- pjsip/include/pjsua-lib/pjsua_internal.h | 3 +- pjsip/src/pjsua-lib/pjsua_core.c | 17 ++ pjsip/src/pjsua-lib/pjsua_pres.c | 431 ++++++++++++++++++++++--------- 5 files changed, 368 insertions(+), 121 deletions(-) (limited to 'pjsip') 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 +#include #include #include #include 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 @@ -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; 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; retryflag = 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_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