summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjlib/include/pj/timer.h9
-rw-r--r--pjlib/src/pj/timer.c5
-rw-r--r--pjsip/src/pjsip/sip_transaction.c15
3 files changed, 29 insertions, 0 deletions
diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h
index 59149385..df6155a8 100644
--- a/pjlib/include/pj/timer.h
+++ b/pjlib/include/pj/timer.h
@@ -213,6 +213,15 @@ PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
pj_timer_heap_callback *cb );
/**
+ * Queries whether a timer entry is currently running.
+ *
+ * @param entry The timer entry to query.
+ *
+ * @return PJ_TRUE if the timer is running. PJ_FALSE if not.
+ */
+PJ_DECL(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry );
+
+/**
* Schedule a timer entry which will expire AFTER the specified delay.
*
* @param ht The timer heap.
diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c
index b9205c81..931a57fa 100644
--- a/pjlib/src/pj/timer.c
+++ b/pjlib/src/pj/timer.c
@@ -466,6 +466,11 @@ PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
return entry;
}
+PJ_DEF(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry )
+{
+ return (entry->_timer_id >= 1);
+}
+
#if PJ_TIMER_DEBUG
static pj_status_t schedule_w_grp_lock_dbg(pj_timer_heap_t *ht,
pj_timer_entry *entry,
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index ec189815..446617e2 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -2224,6 +2224,18 @@ static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
{
pj_status_t status;
+ if (resched && pj_timer_entry_running(&tsx->retransmit_timer)) {
+ /* We've been asked to reschedule but the timer is already rerunning.
+ * This can only happen in a race condition where, between removing
+ * this retransmit timer from the heap and actually scheduling it,
+ * another thread has got in and rescheduled the timer itself. In
+ * this scenario, the transmission has already happened and so we
+ * should just quit out immediately, without either resending the
+ * message or restarting the timer.
+ */
+ return PJ_SUCCESS;
+ }
+
PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);
PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d",
@@ -2332,6 +2344,7 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx,
* timeout.
*/
lock_timer(tsx);
+ tsx_cancel_timer( tsx, &tsx->timeout_timer );
tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val,
TIMEOUT_TIMER);
unlock_timer(tsx);
@@ -2677,6 +2690,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
}
lock_timer(tsx);
+ tsx_cancel_timer(tsx, &tsx->timeout_timer);
tsx_schedule_timer( tsx, &tsx->timeout_timer,
&timeout, TIMEOUT_TIMER);
unlock_timer(tsx);
@@ -2705,6 +2719,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
* non-reliable transports, and zero for reliable transports.
*/
lock_timer(tsx);
+ tsx_cancel_timer(tsx, &tsx->timeout_timer);
if (tsx->method.id == PJSIP_INVITE_METHOD) {
/* Start timer H for INVITE */
tsx_schedule_timer(tsx, &tsx->timeout_timer,