Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5325)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5326)
@@ -3326,5 +3326,5 @@
      * Control the use of STUN for the media transports.
      *
-     * Default: PJSUA_STUN_USE_DEFAULT
+     * Default: PJSUA_STUN_RETRY_ON_FAILURE
      */
     pjsua_stun_use 		media_stun_use;
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 5325)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 5326)
@@ -368,4 +368,6 @@
     pj_stun_resolve_cb	 cb;	    /**< App callback	    */
     pj_bool_t		 blocking;  /**< Blocking?	    */
+    pj_thread_t		*waiter;    /**< Waiting thread	    */
+    pj_timer_entry	 timer;	    /**< Destroy timer	    */
     pj_status_t		 status;    /**< Session status	    */
     pj_sockaddr		 addr;	    /**< Result		    */
@@ -609,5 +611,5 @@
  */
 /* Resolve the STUN server */
-pj_status_t resolve_stun_server(pj_bool_t wait);
+pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error);
 
 /** 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 5325)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 5326)
@@ -303,4 +303,6 @@
     cfg->register_on_acc_add = PJ_TRUE;
     cfg->mwi_expires = PJSIP_MWI_DEFAULT_EXPIRES;
+
+    cfg->media_stun_use = PJSUA_STUN_RETRY_ON_FAILURE;
 }
 
@@ -1050,5 +1052,5 @@
 
     /* Start resolving STUN server */
-    status = resolve_stun_server(PJ_FALSE);
+    status = resolve_stun_server(PJ_FALSE, PJ_FALSE);
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
@@ -1157,11 +1159,38 @@
 }
 
+
+static void destroy_stun_resolve_cb(pj_timer_heap_t *t, pj_timer_entry *e)
+{
+    pjsua_stun_resolve *sess = (pjsua_stun_resolve*)e->user_data;
+    PJ_UNUSED_ARG(t);
+
+    PJSUA_LOCK();
+    pj_list_erase(sess);
+    PJSUA_UNLOCK();
+
+    pj_assert(sess->stun_sock==NULL);
+    pj_pool_release(sess->pool);
+}
+
+
 static void destroy_stun_resolve(pjsua_stun_resolve *sess)
 {
+    pj_time_val timeout = {0, 0};
+
     sess->destroy_flag = PJ_TRUE;
-    if (sess->ref_cnt > 0)
+
+    /* If the STUN resolution session is blocking, only the waiting thread
+     * is allowed to destroy the session, otherwise it may cause deadlock.
+     */
+    if (sess->blocking) {
+	if (sess->waiter != pj_thread_this())
+	    return;
+
+	/* Before destroying, make sure ref count is zero. */
+	while (sess->ref_cnt > 0)
+	    pj_thread_sleep(10);
+
+    } else if (sess->ref_cnt > 0)
 	return;
-
-    PJSUA_LOCK();
 
     if (sess->stun_sock) {
@@ -1176,10 +1205,12 @@
     }
     
-    pj_list_erase(sess);
-
-    PJSUA_UNLOCK();
-
-    pj_assert(sess->stun_sock==NULL);
-    pj_pool_release(sess->pool);
+    /* Schedule session clean up, it needs PJSUA lock and locking it here
+     * may cause deadlock as this function may be called by STUN socket
+     * while holding STUN socket lock, while application may wait for STUN
+     * resolution while holding PJSUA lock.
+     */
+    pj_timer_entry_init(&sess->timer, 0, (void*)sess,
+			&destroy_stun_resolve_cb);
+    pjsua_schedule_timer(&sess->timer, &timeout);
 }
 
@@ -1224,7 +1255,5 @@
     }
 
-    stun_resolve_add_ref(sess);
     sess->cb(&result);
-    stun_resolve_dec_ref(sess);
 
 on_return:
@@ -1258,4 +1287,6 @@
 	sess->stun_sock = NULL;
 
+	stun_resolve_add_ref(sess);
+
 	++sess->idx;
 	if (sess->idx >= sess->count)
@@ -1264,4 +1295,6 @@
 	resolve_stun_entry(sess);
 
+	stun_resolve_dec_ref(sess);
+
 	return PJ_FALSE;
 
@@ -1271,4 +1304,6 @@
 	pj_stun_sock_get_info(stun_sock, &ssi);
 	pj_memcpy(&sess->addr, &ssi.srv_addr, sizeof(sess->addr));
+
+	stun_resolve_add_ref(sess);
 
 	sess->status = PJ_SUCCESS;
@@ -1278,4 +1313,6 @@
 	stun_resolve_complete(sess);
 
+	stun_resolve_dec_ref(sess);
+
 	return PJ_FALSE;
 
@@ -1292,6 +1329,4 @@
 {
     pj_status_t status = PJ_EUNKNOWN;
-
-    stun_resolve_add_ref(sess);
 
     /* Loop while we have entry to try */
@@ -1361,17 +1396,16 @@
 	 * stun_sock_cb()
 	 */
-	goto on_return;
+	return;
     }
 
     if (sess->idx >= sess->count) {
 	/* No more entries to try */
+	stun_resolve_add_ref(sess);
 	pj_assert(status != PJ_SUCCESS || sess->status != PJ_EPENDING);
         if (sess->status == PJ_EPENDING)
             sess->status = status;
 	stun_resolve_complete(sess);
-    }
-
-on_return:
-    stun_resolve_dec_ref(sess);
+	stun_resolve_dec_ref(sess);
+    }
 }
 
@@ -1397,10 +1431,10 @@
     pjsua_var.stun_status = PJ_EUNKNOWN;
 
-    status = resolve_stun_server(wait);
+    PJSUA_UNLOCK();
+    
+    status = resolve_stun_server(wait, PJ_FALSE);
     if (wait == PJ_FALSE && status == PJ_EPENDING)
         status = PJ_SUCCESS;
 
-    PJSUA_UNLOCK();
-    
     return status;
 }
@@ -1419,5 +1453,6 @@
     pjsua_stun_resolve *sess;
     pj_status_t status;
-    unsigned i;
+    unsigned i, max_wait_ms;
+    pj_timestamp start, now;
 
     PJ_ASSERT_RETURN(count && srv && cb, PJ_EINVAL);
@@ -1433,4 +1468,5 @@
     sess->count = count;
     sess->blocking = wait;
+    sess->waiter = pj_thread_this();
     sess->status = PJ_EPENDING;
     sess->srv = (pj_str_t*) pj_pool_calloc(pool, count, sizeof(pj_str_t));
@@ -1447,4 +1483,11 @@
     if (!wait)
 	return PJ_SUCCESS;
+
+    /* Should limit the wait time to avoid deadlock. For example,
+     * if app holds dlg/tsx lock, pjsua worker thread will block on
+     * any dlg/tsx state change.
+     */
+    max_wait_ms = count * pjsua_var.stun_cfg.rto_msec * (1 << 7);
+    pj_get_timestamp(&start);
 
     while (sess->status == PJ_EPENDING) {
@@ -1461,4 +1504,8 @@
             pj_thread_sleep(20);
         }
+
+	pj_get_timestamp(&now);
+	if (pj_elapsed_msec(&start, &now) > max_wait_ms)
+	    sess->status = PJ_ETIMEDOUT;
     }
 
@@ -1528,6 +1575,14 @@
  * Resolve STUN server.
  */
-pj_status_t resolve_stun_server(pj_bool_t wait)
-{
+pj_status_t resolve_stun_server(pj_bool_t wait, pj_bool_t retry_if_cur_error)
+{
+    /* Retry resolving if currently the STUN status is error */
+    if (pjsua_var.stun_status != PJ_EPENDING &&
+	pjsua_var.stun_status != PJ_SUCCESS &&
+	retry_if_cur_error)
+    {
+	pjsua_var.stun_status = PJ_EUNKNOWN;
+    }
+
     if (pjsua_var.stun_status == PJ_EUNKNOWN) {
 	pj_status_t status;
@@ -1557,8 +1612,14 @@
 	 */
 	if (wait) {
-	    pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
-
-	    if (has_pjsua_lock)
-		PJSUA_UNLOCK();
+	    unsigned max_wait_ms;
+	    pj_timestamp start, now;
+
+	    /* Should limit the wait time to avoid deadlock. For example,
+	     * if app holds dlg/tsx lock, pjsua worker thread will block on
+	     * any dlg/tsx state change.
+	     */
+	    max_wait_ms = pjsua_var.ua_cfg.stun_srv_cnt *
+			  pjsua_var.stun_cfg.rto_msec * (1 << 7);
+	    pj_get_timestamp(&start);
 
 	    while (pjsua_var.stun_status == PJ_EPENDING) {		
@@ -1575,7 +1636,9 @@
 		    pj_thread_sleep(10);
                 }
+
+		pj_get_timestamp(&now);
+		if (pj_elapsed_msec(&start, &now) > max_wait_ms)
+		    return PJ_ETIMEDOUT;
 	    }
-	    if (has_pjsua_lock)
-		PJSUA_LOCK();
 	}
     }
@@ -1998,5 +2061,5 @@
 
     /* Make sure STUN server resolution has completed */
-    status = resolve_stun_server(PJ_TRUE);
+    status = resolve_stun_server(PJ_TRUE, PJ_TRUE);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
@@ -2785,5 +2848,5 @@
 
     /* Make sure STUN server resolution has completed */
-    status = resolve_stun_server(PJ_TRUE);
+    status = resolve_stun_server(PJ_TRUE, PJ_TRUE);
     if (status != PJ_SUCCESS) {
 	pjsua_var.nat_status = status;
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5325)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5326)
@@ -255,5 +255,8 @@
     /* Make sure STUN server resolution has completed */
     if (!use_ipv6 && pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
-	status = resolve_stun_server(PJ_TRUE);
+	pj_bool_t retry_stun = (acc->cfg.media_stun_use &
+				PJSUA_STUN_RETRY_ON_FAILURE) ==
+				PJSUA_STUN_RETRY_ON_FAILURE;
+	status = resolve_stun_server(PJ_TRUE, retry_stun);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
@@ -391,5 +394,6 @@
 
 	    if (status != PJ_SUCCESS && pjsua_var.ua_cfg.stun_srv_cnt > 1 &&
-	        ((acc->cfg.media_stun_use & PJSUA_STUN_RETRY_ON_FAILURE)!=0))
+	        ((acc->cfg.media_stun_use & PJSUA_STUN_RETRY_ON_FAILURE)==
+		  PJSUA_STUN_RETRY_ON_FAILURE))
 	    {
 		pj_str_t srv = 
@@ -419,9 +423,5 @@
 	    	status=pjsua_update_stun_servers(pjsua_var.ua_cfg.stun_srv_cnt,
 	    			   	    	 pjsua_var.ua_cfg.stun_srv,
-	    			   	    	 PJ_FALSE);
-
-		if (status == PJ_SUCCESS)
-		    status = resolve_stun_server(PJ_TRUE);
-
+	    			   	    	 PJ_TRUE);
 	    	if (status == PJ_SUCCESS) {
 		    if (pjsua_var.stun_srv.addr.sa_family != 0) {
@@ -829,5 +829,8 @@
     /* Make sure STUN server resolution has completed */
     if (pjsua_media_acc_is_using_stun(call_med->call->acc_id)) {
-	status = resolve_stun_server(PJ_TRUE);
+	pj_bool_t retry_stun = (acc_cfg->media_stun_use &
+				PJSUA_STUN_RETRY_ON_FAILURE) ==
+				PJSUA_STUN_RETRY_ON_FAILURE;
+	status = resolve_stun_server(PJ_TRUE, retry_stun);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
