Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 4357)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 4360)
@@ -98,4 +98,5 @@
 };
 
+#define THIS_FILE		"ice_session.c"
 #define CHECK_NAME_LEN		128
 #define LOG4(expr)		PJ_LOG(4,expr)
@@ -135,4 +136,5 @@
 static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
 static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now);
+static void ice_on_destroy(void *obj);
 static void destroy_ice(pj_ice_sess *ice,
 			pj_status_t reason);
@@ -289,4 +291,5 @@
     status = pj_stun_session_create(&ice->stun_cfg, NULL, 
 			            &sess_cb, PJ_TRUE,
+			            ice->grp_lock,
 				    &comp->stun_sess);
     if (status != PJ_SUCCESS)
@@ -333,4 +336,5 @@
 				       const pj_str_t *local_ufrag,
 				       const pj_str_t *local_passwd,
+				       pj_grp_lock_t *grp_lock,
 				       pj_ice_sess **p_ice)
 {
@@ -360,10 +364,17 @@
 		     name, ice);
 
-    status = pj_mutex_create_recursive(pool, ice->obj_name, 
-				       &ice->mutex);
-    if (status != PJ_SUCCESS) {
-	destroy_ice(ice, status);
-	return status;
-    }
+    if (grp_lock) {
+	ice->grp_lock = grp_lock;
+    } else {
+	status = pj_grp_lock_create(pool, NULL, &ice->grp_lock);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(pool);
+	    return status;
+	}
+    }
+
+    pj_grp_lock_add_ref(ice->grp_lock);
+    pj_grp_lock_add_handler(ice->grp_lock, pool, ice,
+                            &ice_on_destroy);
 
     pj_memcpy(&ice->cb, cb, sizeof(*cb));
@@ -445,4 +456,19 @@
 
 /*
+ * Callback to really destroy the session
+ */
+static void ice_on_destroy(void *obj)
+{
+    pj_ice_sess *ice = (pj_ice_sess*) obj;
+
+    if (ice->pool) {
+	pj_pool_t *pool = ice->pool;
+	ice->pool = NULL;
+	pj_pool_release(pool);
+    }
+    LOG4((THIS_FILE, "ICE session %p destroyed", ice));
+}
+
+/*
  * Destroy
  */
@@ -453,20 +479,18 @@
 
     if (reason == PJ_SUCCESS) {
-	LOG4((ice->obj_name, "Destroying ICE session"));
+	LOG4((ice->obj_name, "Destroying ICE session %p", ice));
+    }
+
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	pj_grp_lock_release(ice->grp_lock);
+	return;
     }
 
     ice->is_destroying = PJ_TRUE;
 
-    /* Let other callbacks finish */
-    if (ice->mutex) {
-	pj_mutex_lock(ice->mutex);
-	pj_mutex_unlock(ice->mutex);
-    }
-
-    if (ice->timer.id) {
-	pj_timer_heap_cancel(ice->stun_cfg.timer_heap, 
-			     &ice->timer);
-	ice->timer.id = PJ_FALSE;
-    }
+    pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+                                   &ice->timer, PJ_FALSE);
 
     for (i=0; i<ice->comp_cnt; ++i) {
@@ -477,19 +501,10 @@
     }
 
-    if (ice->clist.timer.id) {
-	pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
-	ice->clist.timer.id = PJ_FALSE;
-    }
-
-    if (ice->mutex) {
-	pj_mutex_destroy(ice->mutex);
-	ice->mutex = NULL;
-    }
-
-    if (ice->pool) {
-	pj_pool_t *pool = ice->pool;
-	ice->pool = NULL;
-	pj_pool_release(pool);
-    }
+    pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+                                   &ice->clist.timer,
+                                   PJ_FALSE);
+
+    pj_grp_lock_dec_ref(ice->grp_lock);
+    pj_grp_lock_release(ice->grp_lock);
 }
 
@@ -710,5 +725,5 @@
     PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL);
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) {
@@ -750,5 +765,5 @@
 
 on_error:
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
     return status;
 }
@@ -767,5 +782,5 @@
     *cand_id = -1;
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     /* First find in valid list if we have nominated pair */
@@ -775,5 +790,5 @@
 	if (check->lcand->comp_id == comp_id) {
 	    *cand_id = GET_LCAND_ID(check->lcand);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	}
@@ -787,5 +802,5 @@
 	{
 	    *cand_id = GET_LCAND_ID(lcand);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	}
@@ -800,5 +815,5 @@
 	{
 	    *cand_id = GET_LCAND_ID(lcand);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	}
@@ -812,5 +827,5 @@
 	{
 	    *cand_id = GET_LCAND_ID(lcand);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	}
@@ -818,5 +833,5 @@
 
     /* Still no candidate is found! :( */
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
 
     pj_assert(!"Should have a candidate by now");
@@ -1128,11 +1143,17 @@
     pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
     enum timer_type type = (enum timer_type)te->id;
-    pj_bool_t has_mutex = PJ_TRUE;
 
     PJ_UNUSED_ARG(th);
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     te->id = TIMER_NONE;
+
+    if (ice->is_destroying) {
+	/* Stray timer, could happen when destroy is invoked while callback
+	 * is pending. */
+	pj_grp_lock_release(ice->grp_lock);
+	return;
+    }
 
     switch (type) {
@@ -1158,6 +1179,4 @@
 	    ice_status = ice->ice_status;
 	    on_ice_complete = ice->cb.on_ice_complete;
-	    has_mutex = PJ_FALSE;
-	    pj_mutex_unlock(ice->mutex);
 
 	    /* Notify app about ICE completion*/
@@ -1177,6 +1196,5 @@
     }
 
-    if (has_mutex)
-	pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
 }
 
@@ -1236,6 +1254,8 @@
 	pj_time_val_normalize(&delay);
 
-	ice->timer.id = TIMER_KEEP_ALIVE;
-	pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+	pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+	                                  &ice->timer, &delay,
+	                                  TIMER_KEEP_ALIVE,
+	                                  ice->grp_lock);
 
     } else {
@@ -1251,8 +1271,6 @@
 	ice->ice_status = status;
     
-	if (ice->timer.id != TIMER_NONE) {
-	    pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
-	    ice->timer.id = TIMER_NONE;
-	}
+	pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+	                               TIMER_NONE);
 
 	/* Log message */
@@ -1267,7 +1285,8 @@
 	    pj_time_val delay = {0, 0};
 
-	    ice->timer.id = TIMER_COMPLETION_CALLBACK;
-	    pj_timer_heap_schedule(ice->stun_cfg.timer_heap, 
-				   &ice->timer, &delay);
+	    pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+	                                      &ice->timer, &delay,
+	                                      TIMER_COMPLETION_CALLBACK,
+	                                      ice->grp_lock);
 	}
     }
@@ -1497,8 +1516,9 @@
 		    pj_time_val_normalize(&delay);
 
-		    ice->timer.id = TIMER_CONTROLLED_WAIT_NOM;
-		    pj_timer_heap_schedule(ice->stun_cfg.timer_heap, 
-					   &ice->timer,
-					   &delay);
+		    pj_timer_heap_schedule_w_grp_lock(
+					ice->stun_cfg.timer_heap,
+		                        &ice->timer, &delay,
+		                        TIMER_CONTROLLED_WAIT_NOM,
+		                        ice->grp_lock);
 
 		    LOG5((ice->obj_name, 
@@ -1576,8 +1596,6 @@
 	      ice->opt.nominated_check_delay));
 
-	if (ice->timer.id != TIMER_NONE) {
-	    pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
-	    ice->timer.id = TIMER_NONE;
-	}
+	pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+	                               TIMER_NONE);
 
 	/* All components have valid pair. Let connectivity checks run for
@@ -1588,6 +1606,8 @@
 	pj_time_val_normalize(&delay);
 
-	ice->timer.id = TIMER_START_NOMINATED_CHECK;
-	pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
+	pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+	                                  &ice->timer, &delay,
+	                                  TIMER_START_NOMINATED_CHECK,
+	                                  ice->grp_lock);
 	return PJ_FALSE;
     }
@@ -1619,5 +1639,5 @@
 		     PJ_ETOOMANY);
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     /* Save credentials */
@@ -1667,5 +1687,5 @@
 
 	    if (clist->count >= PJ_ICE_MAX_CHECKS) {
-		pj_mutex_unlock(ice->mutex);
+		pj_grp_lock_release(ice->grp_lock);
 		return PJ_ETOOMANY;
 	    } 
@@ -1695,5 +1715,5 @@
     if (clist->count == 0) {
 	LOG4((ice->obj_name,  "Error: no checklist can be created"));
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return PJ_ENOTFOUND;
     }
@@ -1705,5 +1725,5 @@
     status = prune_checklist(ice, clist);
     if (status != PJ_SUCCESS) {
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return status;
     }
@@ -1732,5 +1752,5 @@
     dump_checklist("Checklist created:", ice, clist);
 
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
 
     return PJ_SUCCESS;
@@ -1851,11 +1871,8 @@
     clist = td->clist;
 
-    if (ice->is_destroying)
-	return PJ_SUCCESS;
-
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     if (ice->is_destroying) {
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return PJ_SUCCESS;
     }
@@ -1879,5 +1896,5 @@
 	    status = perform_check(ice, clist, i, ice->is_nominating);
 	    if (status != PJ_SUCCESS) {
-		pj_mutex_unlock(ice->mutex);
+		pj_grp_lock_release(ice->grp_lock);
 		pj_log_pop_indent();
 		return status;
@@ -1899,5 +1916,5 @@
 		status = perform_check(ice, clist, i, ice->is_nominating);
 		if (status != PJ_SUCCESS) {
-		    pj_mutex_unlock(ice->mutex);
+		    pj_grp_lock_release(ice->grp_lock);
 		    pj_log_pop_indent();
 		    return status;
@@ -1916,10 +1933,10 @@
 	pj_time_val timeout = {0, PJ_ICE_TA_VAL};
 
-	te->id = PJ_TRUE;
 	pj_time_val_normalize(&timeout);
-	pj_timer_heap_schedule(th, te, &timeout);
-    }
-
-    pj_mutex_unlock(ice->mutex);
+	pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE,
+	                                  ice->grp_lock);
+    }
+
+    pj_grp_lock_release(ice->grp_lock);
     pj_log_pop_indent();
     return PJ_SUCCESS;
@@ -1941,6 +1958,6 @@
     /* Stop our timer if it's active */
     if (ice->timer.id == TIMER_START_NOMINATED_CHECK) {
-	pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
-	ice->timer.id = TIMER_NONE;
+	pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap, &ice->timer,
+	                               TIMER_NONE);
     }
 
@@ -1970,16 +1987,13 @@
 
     /* And (re)start the periodic check */
-    if (ice->clist.timer.id) {
-	pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
-	ice->clist.timer.id = PJ_FALSE;
-    }
-
-    ice->clist.timer.id = PJ_TRUE;
+    pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
+                                   &ice->clist.timer, PJ_FALSE);
+
     delay.sec = delay.msec = 0;
-    status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, 
-				    &ice->clist.timer, &delay);
-    if (status != PJ_SUCCESS) {
-	ice->clist.timer.id = PJ_FALSE;
-    } else {
+    status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+                                               &ice->clist.timer, &delay,
+                                               PJ_TRUE,
+                                               ice->grp_lock);
+    if (status == PJ_SUCCESS) {
 	LOG5((ice->obj_name, "Periodic timer rescheduled.."));
     }
@@ -2031,5 +2045,5 @@
 
     /* Lock session */
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
 
     LOG4((ice->obj_name, "Starting ICE check.."));
@@ -2061,5 +2075,5 @@
     if (i == clist->count) {
 	pj_assert(!"Unable to find checklist for component 1");
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	pj_log_pop_indent();
 	return PJNATH_EICEINCOMPID;
@@ -2115,13 +2129,13 @@
      * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
      */
-    clist->timer.id = PJ_TRUE;
     delay.sec = delay.msec = 0;
-    status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, 
-				    &clist->timer, &delay);
+    status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+                                               &clist->timer, &delay,
+                                               PJ_TRUE, ice->grp_lock);
     if (status != PJ_SUCCESS) {
 	clist->timer.id = PJ_FALSE;
     }
 
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
     pj_log_pop_indent();
     return status;
@@ -2144,7 +2158,20 @@
     pj_ice_sess *ice = sd->ice;
     pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token;
+    pj_status_t status;
     
-    return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
-				pkt, pkt_size, dst_addr, addr_len);
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	/* Stray retransmit timer that could happen while
+	 * we're being destroyed */
+	pj_grp_lock_release(ice->grp_lock);
+	return PJ_EINVALIDOP;
+    }
+
+    status = (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id,
+				  pkt, pkt_size, dst_addr, addr_len);
+
+    pj_grp_lock_release(ice->grp_lock);
+    return status;
 }
 
@@ -2181,5 +2208,11 @@
     check->tdata = NULL;
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	/* Not sure if this is possible but just in case */
+	pj_grp_lock_release(ice->grp_lock);
+	return;
+    }
 
     /* Init lcand to NULL. lcand will be found from the mapped address
@@ -2232,5 +2265,5 @@
 			  check->nominated || ice->is_nominating);
 	    pj_log_pop_indent();
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return;
 	}
@@ -2247,5 +2280,5 @@
 	on_check_complete(ice, check);
 	pj_log_pop_indent();
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return;
     }
@@ -2271,5 +2304,5 @@
 	on_check_complete(ice, check);
 	pj_log_pop_indent();
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return;
     }
@@ -2304,5 +2337,5 @@
 			PJNATH_ESTUNNOMAPPEDADDR);
 	on_check_complete(ice, check);
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return;
     }
@@ -2352,5 +2385,5 @@
 			    status);
 	    on_check_complete(ice, check);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return;
 	}
@@ -2412,9 +2445,9 @@
     if (on_check_complete(ice, check)) {
 	/* ICE complete! */
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return;
     }
 
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
 }
 
@@ -2457,5 +2490,10 @@
     ice = sd->ice;
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	pj_grp_lock_release(ice->grp_lock);
+	return PJ_EINVALIDOP;
+    }
 
     /*
@@ -2472,5 +2510,5 @@
     if (prio_attr == NULL) {
 	LOG5((ice->obj_name, "Received Binding request with no PRIORITY"));
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return PJ_SUCCESS;
     }
@@ -2517,5 +2555,5 @@
 				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	}
@@ -2529,5 +2567,5 @@
 				    NULL, token, PJ_TRUE, 
 				    src_addr, src_addr_len);
-	    pj_mutex_unlock(ice->mutex);
+	    pj_grp_lock_release(ice->grp_lock);
 	    return PJ_SUCCESS;
 	} else {
@@ -2544,5 +2582,5 @@
     status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata);
     if (status != PJ_SUCCESS) {
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return status;
     }
@@ -2596,5 +2634,5 @@
     }
 
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
     return PJ_SUCCESS;
 }
@@ -2885,10 +2923,15 @@
     }
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	pj_grp_lock_release(ice->grp_lock);
+	return PJ_EINVALIDOP;
+    }
 
     comp = find_comp(ice, comp_id);
     if (comp == NULL) {
 	status = PJNATH_EICEINCOMPID;
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	goto on_return;
     }
@@ -2896,5 +2939,5 @@
     if (comp->valid_check == NULL) {
 	status = PJNATH_EICEINPROGRESS;
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	goto on_return;
     }
@@ -2905,5 +2948,7 @@
 
     /* Release the mutex now to avoid deadlock (see ticket #1451). */
-    pj_mutex_unlock(ice->mutex);
+    pj_grp_lock_release(ice->grp_lock);
+
+    PJ_RACE_ME(5);
 
     status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, 
@@ -2932,9 +2977,14 @@
     PJ_ASSERT_RETURN(ice, PJ_EINVAL);
 
-    pj_mutex_lock(ice->mutex);
+    pj_grp_lock_acquire(ice->grp_lock);
+
+    if (ice->is_destroying) {
+	pj_grp_lock_release(ice->grp_lock);
+	return PJ_EINVALIDOP;
+    }
 
     comp = find_comp(ice, comp_id);
     if (comp == NULL) {
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return PJNATH_EICEINCOMPID;
     }
@@ -2949,5 +2999,5 @@
     if (msg_data == NULL) {
 	pj_assert(!"Invalid transport ID");
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
 	return PJ_EINVAL;
     }
@@ -2969,10 +3019,12 @@
 		  ice->tmp.errmsg));
 	}
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
     } else {
 	/* Not a STUN packet. Call application's callback instead, but release
 	 * the mutex now or otherwise we may get deadlock.
 	 */
-	pj_mutex_unlock(ice->mutex);
+	pj_grp_lock_release(ice->grp_lock);
+
+	PJ_RACE_ME(5);
 
 	(*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, 
