Index: /pjproject/trunk/pjnath/include/pjnath/stun_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 6068)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 6069)
@@ -349,4 +349,6 @@
     pj_uint32_t		 msg_magic;	/**< Message magic.		    */
     pj_uint8_t		 msg_key[12];	/**< Message/transaction key.	    */
+    
+    pj_grp_lock_t	*grp_lock;	/**< Group lock (for resp cache).   */
 
     pj_stun_req_cred_info auth_info;	/**< Credential info		    */
Index: /pjproject/trunk/pjnath/src/pjnath/stun_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 6068)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 6069)
@@ -77,4 +77,5 @@
 static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx);
 static void stun_sess_on_destroy(void *comp);
+static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force);
 
 static pj_stun_tsx_cb tsx_cb = 
@@ -154,5 +155,5 @@
         pj_grp_lock_acquire(sess->grp_lock);
 	tsx_erase(sess, tdata);
-	pj_pool_release(tdata->pool);
+	destroy_tdata(tdata, PJ_TRUE);
 	pj_grp_lock_release(sess->grp_lock);
     }
@@ -161,4 +162,11 @@
 
     TRACE_((THIS_FILE, "STUN transaction %p destroyed", tsx));
+}
+
+static void tdata_on_destroy(void *arg)
+{
+    pj_stun_tx_data *tdata = (pj_stun_tx_data*)arg;
+
+    pj_pool_safe_release(&tdata->pool);
 }
 
@@ -168,8 +176,9 @@
 	    force, tdata->client_tsx));
 
+    /* STUN session may have been destroyed, except when tdata is cached. */
+
     if (tdata->res_timer.id != PJ_FALSE) {
 	pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
-	                               &tdata->res_timer, PJ_FALSE);
-	pj_list_erase(tdata);
+				       &tdata->res_timer, PJ_FALSE);
     }
 
@@ -180,5 +189,10 @@
 	    pj_stun_client_tsx_set_data(tdata->client_tsx, NULL);
 	}
-	pj_pool_release(tdata->pool);
+	if (tdata->grp_lock) {
+	    pj_grp_lock_dec_ref(tdata->sess->grp_lock);
+	    pj_grp_lock_dec_ref(tdata->grp_lock);
+	} else {
+	    tdata_on_destroy(tdata);
+	}
 
     } else {
@@ -189,5 +203,11 @@
 
 	} else {
-	    pj_pool_release(tdata->pool);
+	    pj_list_erase(tdata);
+	    if (tdata->grp_lock) {
+		pj_grp_lock_dec_ref(tdata->sess->grp_lock);
+		pj_grp_lock_dec_ref(tdata->grp_lock);
+	    } else {
+		tdata_on_destroy(tdata);
+	    }
 	}
     }
@@ -226,8 +246,7 @@
     PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
 
-    pj_list_erase(tdata);
+    destroy_tdata(tdata, PJ_FALSE);
     pj_grp_lock_release(sess->grp_lock);
 
-    destroy_tdata(tdata, PJ_FALSE);
 }
 
@@ -565,15 +584,6 @@
     }
 
-    while (!pj_list_empty(&sess->cached_response_list)) {
-	pj_stun_tx_data *tdata = sess->cached_response_list.next;
-	destroy_tdata(tdata, PJ_TRUE);
-    }
-
-    if (sess->rx_pool) {
-	pj_pool_release(sess->rx_pool);
-	sess->rx_pool = NULL;
-    }
-
-    pj_pool_release(sess->pool);
+    pj_pool_safe_release(&sess->rx_pool);
+    pj_pool_safe_release(&sess->pool);
 
     TRACE_((THIS_FILE, "STUN session %p destroyed", sess));
@@ -599,5 +609,5 @@
     sess->is_destroying = PJ_TRUE;
 
-    /* We need to stop transactions and cached response because they are
+    /* We need to stop transactions because they are
      * holding the group lock's reference counter while retransmitting.
      */
@@ -609,9 +619,10 @@
     }
 
-    tdata = sess->cached_response_list.next;
-    while (tdata != &sess->cached_response_list) {
-	pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
-				       &tdata->res_timer, PJ_FALSE);
-	tdata = tdata->next;
+    /* Destroy cached response within session lock protection to avoid
+     * race scenario with on_cache_timeout().
+     */
+    while (!pj_list_empty(&sess->cached_response_list)) {
+	pj_stun_tx_data *tdata = sess->cached_response_list.next;
+	destroy_tdata(tdata, PJ_TRUE);
     }
 
@@ -805,5 +816,5 @@
 on_error:
     if (tdata)
-	pj_pool_release(tdata->pool);
+	pj_pool_safe_release(&tdata->pool);
     pj_grp_lock_release(sess->grp_lock);
     return status;
@@ -836,5 +847,5 @@
 				NULL, &tdata->msg);
     if (status != PJ_SUCCESS) {
-	pj_pool_release(tdata->pool);
+	pj_pool_safe_release(&tdata->pool);
 	pj_grp_lock_release(sess->grp_lock);
 	return status;
@@ -875,5 +886,5 @@
 					 err_code, err_msg, &tdata->msg);
     if (status != PJ_SUCCESS) {
-	pj_pool_release(tdata->pool);
+	pj_pool_safe_release(&tdata->pool);
 	pj_grp_lock_release(sess->grp_lock);
 	return status;
@@ -1008,4 +1019,5 @@
 
     } else {
+	/* Otherwise for non-request message, send directly to transport. */
 	if (cache_res && 
 	    (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) ||
@@ -1014,4 +1026,19 @@
 	    /* Requested to keep the response in the cache */
 	    pj_time_val timeout;
+
+	    status = pj_grp_lock_create(tdata->pool, NULL, &tdata->grp_lock);
+	    if (status != PJ_SUCCESS) {
+		pj_stun_msg_destroy_tdata(sess, tdata);
+		LOG_ERR_(sess, "Error creating group lock", status);
+		goto on_return;
+	    }
+	    pj_grp_lock_add_ref(tdata->grp_lock);
+	    pj_grp_lock_add_handler(tdata->grp_lock, tdata->pool, tdata,
+				    &tdata_on_destroy);
+
+	    /* Also add ref session group lock to make sure that the session
+	     * is still valid when cache timeout callback is called.
+	     */
+	    pj_grp_lock_add_ref(sess->grp_lock);
 	    
 	    pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
@@ -1025,5 +1052,5 @@
 	                                               &tdata->res_timer,
 	                                               &timeout, PJ_TRUE,
-	                                               sess->grp_lock);
+	                                               tdata->grp_lock);
 	    if (status != PJ_SUCCESS) {
 		pj_stun_msg_destroy_tdata(sess, tdata);
@@ -1034,6 +1061,6 @@
 	    pj_list_push_back(&sess->cached_response_list, tdata);
 	}
-    
-	/* Otherwise for non-request message, send directly to transport. */
+
+	/* Send to transport directly. */
 	status = sess->cb.on_send_msg(sess, token, tdata->pkt, 
 				      tdata->pkt_size, server, addr_len);
