Index: pjproject/trunk/pjnath/src/pjnath/stun_transaction.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 4352)
+++ pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 4360)
@@ -27,4 +27,6 @@
 
 
+#define THIS_FILE		"stun_transaction.c"
+#define TIMER_INACTIVE		0
 #define TIMER_ACTIVE		1
 
@@ -35,4 +37,5 @@
     pj_stun_tsx_cb	 cb;
     void		*user_data;
+    pj_grp_lock_t	*grp_lock;
 
     pj_bool_t		 complete;
@@ -52,4 +55,11 @@
 
 
+#if 1
+#   define TRACE_(expr)		    PJ_LOG(5,expr)
+#else
+#   define TRACE_(expr)
+#endif
+
+
 static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, 
 				      pj_timer_entry *timer);
@@ -57,6 +67,4 @@
 				   pj_timer_entry *timer);
 
-#define stun_perror(tsx,msg,rc) pjnath_perror(tsx->obj_name, msg, rc)
-
 /*
  * Create a STUN client transaction.
@@ -64,4 +72,5 @@
 PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
 					      pj_pool_t *pool,
+					      pj_grp_lock_t *grp_lock,
 					      const pj_stun_tsx_cb *cb,
 					      pj_stun_client_tsx **p_tsx)
@@ -75,4 +84,5 @@
     tsx->rto_msec = cfg->rto_msec;
     tsx->timer_heap = cfg->timer_heap;
+    tsx->grp_lock = grp_lock;
     pj_memcpy(&tsx->cb, cb, sizeof(*cb));
 
@@ -83,5 +93,5 @@
     tsx->destroy_timer.user_data = tsx;
 
-    pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx);
+    pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx);
 
     *p_tsx = tsx;
@@ -101,24 +111,28 @@
     PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL);
 
+    pj_grp_lock_acquire(tsx->grp_lock);
+
     /* Cancel previously registered timer */
-    if (tsx->destroy_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
-	tsx->destroy_timer.id = 0;
-    }
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
+                                   TIMER_INACTIVE);
 
     /* Stop retransmission, just in case */
-    if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
-	tsx->retransmit_timer.id = 0;
-    }
-
-    status = pj_timer_heap_schedule(tsx->timer_heap,
-				    &tsx->destroy_timer, delay);
-    if (status != PJ_SUCCESS)
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+                                   TIMER_INACTIVE);
+
+    status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+                                               &tsx->destroy_timer, delay,
+                                               TIMER_ACTIVE, tsx->grp_lock);
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(tsx->grp_lock);
 	return status;
-
-    tsx->destroy_timer.id = TIMER_ACTIVE;
+    }
+
     tsx->cb.on_complete = NULL;
 
+    pj_grp_lock_release(tsx->grp_lock);
+
+    TRACE_((tsx->obj_name, "STUN transaction %p schedule destroy", tsx));
+
     return PJ_SUCCESS;
 }
@@ -128,18 +142,19 @@
  * Destroy transaction immediately.
  */
-PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx)
+PJ_DEF(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx)
 {
     PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
 
-    if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
-	tsx->retransmit_timer.id = 0;
-    }
-    if (tsx->destroy_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
-	tsx->destroy_timer.id = 0;
-    }
-
-    PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed"));
+    /* Don't call grp_lock_acquire() because we might be called on
+     * group lock's destructor.
+     */
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+                                   TIMER_INACTIVE);
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
+                                   TIMER_INACTIVE);
+
+    PJ_LOG(5,(tsx->obj_name, "STUN client transaction %p stopped, ref_cnt=%d",
+	      tsx, pj_grp_lock_get_ref(tsx->grp_lock)));
+
     return PJ_SUCCESS;
 }
@@ -186,5 +201,5 @@
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0 ||
+    PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE ||
 		     !tsx->require_retransmit, PJ_EBUSY);
 
@@ -212,12 +227,13 @@
 	 * cancel transmission).
 	 */;
-	status = pj_timer_heap_schedule(tsx->timer_heap, 
-					&tsx->retransmit_timer,
-					&tsx->retransmit_time);
+	status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+						   &tsx->retransmit_timer,
+						   &tsx->retransmit_time,
+						   TIMER_ACTIVE,
+						   tsx->grp_lock);
 	if (status != PJ_SUCCESS) {
-	    tsx->retransmit_timer.id = 0;
+	    tsx->retransmit_timer.id = TIMER_INACTIVE;
 	    return status;
 	}
-	tsx->retransmit_timer.id = TIMER_ACTIVE;
     }
 
@@ -236,10 +252,10 @@
 	/* We've been destroyed, don't access the object. */
     } else if (status != PJ_SUCCESS) {
-	if (tsx->retransmit_timer.id != 0 && mod_count) {
-	    pj_timer_heap_cancel(tsx->timer_heap, 
-				 &tsx->retransmit_timer);
-	    tsx->retransmit_timer.id = 0;
-	}
-	stun_perror(tsx, "STUN error sending message", status);
+	if (mod_count) {
+		pj_timer_heap_cancel_if_active( tsx->timer_heap,
+	                               		&tsx->retransmit_timer,
+	                               		TIMER_INACTIVE);
+	}
+	PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message"));
     }
 
@@ -261,4 +277,6 @@
     PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL);
     PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY);
+
+    pj_grp_lock_acquire(tsx->grp_lock);
 
     /* Encode message */
@@ -287,12 +305,14 @@
 	 * cancel transmission).
 	 */;
-	status = pj_timer_heap_schedule(tsx->timer_heap, 
-					&tsx->retransmit_timer,
-					&tsx->retransmit_time);
+	status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+	                                           &tsx->retransmit_timer,
+	                                           &tsx->retransmit_time,
+	                                           TIMER_ACTIVE,
+	                                           tsx->grp_lock);
 	if (status != PJ_SUCCESS) {
-	    tsx->retransmit_timer.id = 0;
+	    tsx->retransmit_timer.id = TIMER_INACTIVE;
+	    pj_grp_lock_release(tsx->grp_lock);
 	    return status;
 	}
-	tsx->retransmit_timer.id = TIMER_ACTIVE;
     }
 
@@ -300,12 +320,12 @@
     status = tsx_transmit_msg(tsx, PJ_TRUE);
     if (status != PJ_SUCCESS) {
-	if (tsx->retransmit_timer.id != 0) {
-	    pj_timer_heap_cancel(tsx->timer_heap, 
-				 &tsx->retransmit_timer);
-	    tsx->retransmit_timer.id = 0;
-	}
+	pj_timer_heap_cancel_if_active(tsx->timer_heap,
+	                               &tsx->retransmit_timer,
+	                               TIMER_INACTIVE);
+	pj_grp_lock_release(tsx->grp_lock);
 	return status;
     }
 
+    pj_grp_lock_release(tsx->grp_lock);
     return PJ_SUCCESS;
 }
@@ -320,4 +340,5 @@
 
     PJ_UNUSED_ARG(timer_heap);
+    pj_grp_lock_acquire(tsx->grp_lock);
 
     if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
@@ -332,4 +353,5 @@
 	    }
 	}
+	pj_grp_lock_release(tsx->grp_lock);
 	/* We might have been destroyed, don't try to access the object */
 	pj_log_pop_indent();
@@ -339,7 +361,5 @@
     tsx->retransmit_timer.id = 0;
     status = tsx_transmit_msg(tsx, PJ_TRUE);
-    if (status == PJNATH_ESTUNDESTROYED) {
-	/* We've been destroyed, don't try to access the object */
-    } else if (status != PJ_SUCCESS) {
+    if (status != PJ_SUCCESS) {
 	tsx->retransmit_timer.id = 0;
 	if (!tsx->complete) {
@@ -349,6 +369,8 @@
 	    }
 	}
-	/* We might have been destroyed, don't try to access the object */
-    }
+    }
+
+    pj_grp_lock_release(tsx->grp_lock);
+    /* We might have been destroyed, don't try to access the object */
 }
 
@@ -363,8 +385,6 @@
     }
 
-    if (tsx->retransmit_timer.id != 0) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
-	tsx->retransmit_timer.id = 0;
-    }
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+                                   TIMER_INACTIVE);
 
     return tsx_transmit_msg(tsx, mod_count);
@@ -380,4 +400,5 @@
 
     tsx->destroy_timer.id = PJ_FALSE;
+
     tsx->cb.on_destroy(tsx);
     /* Don't access transaction after this */
@@ -409,8 +430,6 @@
      * We can cancel retransmit timer now.
      */
-    if (tsx->retransmit_timer.id) {
-	pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
-	tsx->retransmit_timer.id = 0;
-    }
+    pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+                                   TIMER_INACTIVE);
 
     /* Find STUN error code attribute */
