Index: pjproject/branches/projects/uwp/pjsip/src/pjsip/sip_transaction.c
===================================================================
--- pjproject/branches/projects/uwp/pjsip/src/pjsip/sip_transaction.c	(revision 5208)
+++ pjproject/branches/projects/uwp/pjsip/src/pjsip/sip_transaction.c	(revision 5513)
@@ -139,4 +139,10 @@
 #define TRANSPORT_ERR_TIMER	3
 
+/* Flags for tsx_set_state() */
+enum
+{
+    NO_NOTIFY = 1,
+    NO_SCHEDULE_HANDLER = 2,
+};
 
 /* Prototypes. */
@@ -170,9 +176,6 @@
             	                  pjsip_tsx_state_e state,
             	                  pjsip_event_id_e event_src_type,
-            	                  void *event_src );
-static void 	   tsx_set_state_no_notify( pjsip_transaction *tsx,
-            	                            pjsip_tsx_state_e state,
-            	                            pjsip_event_id_e event_src_type,
-            	                            void *event_src );
+            	                  void *event_src,
+				  int flag);
 static void 	   tsx_set_status_code(pjsip_transaction *tsx,
             	                       int code, const pj_str_t *reason);
@@ -1104,4 +1107,5 @@
 	if (tsx->state < PJSIP_TSX_STATE_TERMINATED) {
 	    pjsip_tsx_state_e prev_state;
+	    pj_time_val timeout = { 0, 0 };
 
 	    pj_grp_lock_acquire(tsx->grp_lock);
@@ -1123,6 +1127,13 @@
 	     * https://trac.pjsip.org/repos/ticket/1646
 	     */
-	    tsx_set_state_no_notify( tsx, PJSIP_TSX_STATE_TERMINATED,
-	                             PJSIP_EVENT_TRANSPORT_ERROR, NULL);
+	    /* Also don't schedule tsx handler, otherwise we'll get race
+	     * condition of TU notifications due to delayed TERMINATED
+	     * state TU notification. It happened in multiple worker threads
+	     * environment between TERMINATED & DESTROYED! See:
+	     * https://trac.pjsip.org/repos/ticket/1902
+	     */
+	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+	                   PJSIP_EVENT_TRANSPORT_ERROR, NULL,
+			   NO_NOTIFY | NO_SCHEDULE_HANDLER);
 	    pj_grp_lock_release(tsx->grp_lock);
 
@@ -1139,4 +1150,8 @@
 		(*tsx->tsx_user->on_tsx_state)(tsx, &e);
 	    }
+
+	    /* Now let's schedule the tsx handler */
+	    tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout,
+			       TIMEOUT_TIMER);
 	}
     } else {
@@ -1168,5 +1183,6 @@
 			   pjsip_tsx_state_e state,
 			   pjsip_event_id_e event_src_type,
-                           void *event_src )
+                           void *event_src,
+			   int flag)
 {
     pjsip_tsx_state_e prev_state = tsx->state;
@@ -1193,5 +1209,7 @@
      * rx event.
      */
-    if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) {
+    if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user &&
+	(flag & NO_NOTIFY)==0)
+    {
 	pjsip_rx_data *rdata = (pjsip_rx_data*) event_src;
 
@@ -1207,5 +1225,7 @@
 
     /* Inform TU about state changed. */
-    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
+    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state &&
+	(flag & NO_NOTIFY) == 0)
+    {
 	pjsip_event e;
 	PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src,
@@ -1219,5 +1239,5 @@
      */
     if (state == PJSIP_TSX_STATE_TERMINATED) {
-	pj_time_val timeout = {0, 0};
+	pj_time_val timeout = { 0, 0 };
 
 	/* If we're still waiting for a message to be sent.. */
@@ -1237,5 +1257,8 @@
 	lock_timer(tsx);
 	tsx_cancel_timer(tsx, &tsx->timeout_timer);
-	tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER);
+	if ((flag & NO_SCHEDULE_HANDLER) == 0) {
+	    tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout,
+			       TIMEOUT_TIMER);
+	}
 	unlock_timer(tsx);
 
@@ -1250,16 +1273,4 @@
 
     pj_log_pop_indent();
-}
-
-/* Set transaction state without notifying tsx_user */
-static void tsx_set_state_no_notify( pjsip_transaction *tsx,
-                                     pjsip_tsx_state_e state,
-                                     pjsip_event_id_e event_src_type,
-                                     void *event_src )
-{
-    pjsip_module *tsx_user = tsx->tsx_user;
-    tsx->tsx_user = NULL;
-    tsx_set_state(tsx, state, event_src_type, event_src);
-    tsx->tsx_user = tsx_user;
 }
 
@@ -1627,5 +1638,6 @@
     if (tsx->state < PJSIP_TSX_STATE_TERMINATED) {
         tsx_set_status_code(tsx, code, NULL);
-        tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL);
+        tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER,
+		       NULL, 0);
     }
     pj_grp_lock_release(tsx->grp_lock);
@@ -1838,5 +1850,5 @@
 	if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) {
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
-			   PJSIP_EVENT_UNKNOWN, NULL );
+			   PJSIP_EVENT_UNKNOWN, NULL, 0 );
 	    pj_grp_lock_release(tsx->grp_lock);
 	    return;
@@ -1887,5 +1899,5 @@
 	    err =pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg));
 
-	    PJ_LOG(2,(tsx->obj_name,
+	    PJ_LOG(3,(tsx->obj_name,
 		      "Failed to send %s! err=%d (%s)",
 		      pjsip_tx_data_get_info(send_state->tdata), -sent,
@@ -1913,5 +1925,6 @@
 	    {
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-			       PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
+			       PJSIP_EVENT_TRANSPORT_ERROR,
+			       send_state->tdata, 0);
 	    } 
 	    /* Don't forget to destroy if we have pending destroy flag
@@ -1921,9 +1934,10 @@
 	    {
 		tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, 
-			       PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
+			       PJSIP_EVENT_TRANSPORT_ERROR,
+			       send_state->tdata, 0);
 	    }
 
 	} else {
-	    PJ_PERROR(2,(tsx->obj_name, (pj_status_t)-sent,
+	    PJ_PERROR(3,(tsx->obj_name, (pj_status_t)-sent,
 		         "Temporary failure in sending %s, "
 		         "will try next server",
@@ -2123,5 +2137,5 @@
 	tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-		       PJSIP_EVENT_TRANSPORT_ERROR, NULL );
+		       PJSIP_EVENT_TRANSPORT_ERROR, NULL, 0 );
 
 	return status;
@@ -2355,5 +2369,5 @@
 		    PJSIP_REQUEST_MSG);
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG,
-		       event->body.rx_msg.rdata);
+		       event->body.rx_msg.rdata, 0);
 
     } else {
@@ -2410,5 +2424,5 @@
 	/* Move state. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, 
-                       PJSIP_EVENT_TX_MSG, tdata);
+                       PJSIP_EVENT_TX_MSG, tdata, 0);
     }
 
@@ -2451,5 +2465,5 @@
 	/* Inform TU. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
-                       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+                       PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0);
 
 	/* Transaction is destroyed */
@@ -2570,5 +2584,5 @@
 
 	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
-                       PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
+                       PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata, 0);
 
     }
@@ -2651,5 +2665,5 @@
 
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
-                           PJSIP_EVENT_TX_MSG, tdata );
+                           PJSIP_EVENT_TX_MSG, tdata, 0 );
 
 	    /* Retransmit provisional response every 1 minute if this is
@@ -2685,5 +2699,5 @@
 		 */
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                               PJSIP_EVENT_TX_MSG, tdata );
+                               PJSIP_EVENT_TX_MSG, tdata, 0 );
 
 		/* Transaction is destroyed. */
@@ -2743,5 +2757,5 @@
 		/* Set state to "Completed" */
 		tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
-                               PJSIP_EVENT_TX_MSG, tdata );
+                               PJSIP_EVENT_TX_MSG, tdata, 0 );
 	    }
 
@@ -2802,5 +2816,5 @@
 	    /* Inform TU */
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
-                           PJSIP_EVENT_TX_MSG, tdata );
+                           PJSIP_EVENT_TX_MSG, tdata, 0 );
 
 	} else {
@@ -2836,5 +2850,5 @@
 
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
-                       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+                       PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0);
 
 	return PJ_EBUG;
@@ -2901,5 +2915,5 @@
 	/* Inform the message to TU. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, 
-                       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+                       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 );
 
     } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
@@ -2915,5 +2929,5 @@
 	if (tsx->method.id == PJSIP_INVITE_METHOD) {
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 );
 	    //return PJSIP_ETSXDESTROYED;
 
@@ -2942,5 +2956,5 @@
 	    /* Move state to Completed, inform TU. */
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
-                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 );
 	}
 
@@ -2950,5 +2964,5 @@
 	/* Inform TU. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
-		       PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+		       PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0);
 
 
@@ -3041,5 +3055,5 @@
 	/* Inform TU. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, 
-		       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata);
+		       PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0);
 
 	/* Generate and send ACK for INVITE. */
@@ -3152,5 +3166,5 @@
 	    /* Move state to "Confirmed" */
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, 
-                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+                           PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 );
 	}	
 
@@ -3176,5 +3190,5 @@
 
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                               PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+                               PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 );
 
 		//return PJSIP_ETSXDESTROYED;
@@ -3183,5 +3197,5 @@
 		/* Transaction terminated, it can now be deleted. */
 		tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, 
-                               PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+                               PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 );
 		//return PJSIP_ETSXDESTROYED;
 	    }
@@ -3216,5 +3230,5 @@
 	/* Move to Terminated state. */
 	tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
-                       PJSIP_EVENT_TIMER, event->body.timer.entry );
+                       PJSIP_EVENT_TIMER, event->body.timer.entry, 0 );
 
 	/* Transaction has been destroyed. */
@@ -3291,5 +3305,5 @@
 	    /* Move to Terminated state. */
 	    tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
-			   PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+			   PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 );
 
 	    /* Transaction has been destroyed. */
@@ -3322,5 +3336,5 @@
     /* Destroy this transaction */
     tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, 
-                  event->type, event->body.user.user1 );
+                  event->type, event->body.user.user1, 0 );
 
     return PJ_SUCCESS;
