Index: pjproject/trunk/pjnath/src/pjnath/stun_sock.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 4344)
+++ pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 4360)
@@ -29,7 +29,13 @@
 #include <pj/ip_helper.h>
 #include <pj/log.h>
+#include <pj/os.h>
 #include <pj/pool.h>
 #include <pj/rand.h>
 
+#if 1
+#  define TRACE_(x)	PJ_LOG(5,x)
+#else
+#  define TRACE_(x)
+#endif
 
 enum { MAX_BIND_RETRY = 100 };
@@ -40,5 +46,5 @@
     pj_pool_t		*pool;		/* Pool			    */
     void		*user_data;	/* Application user data    */
-
+    pj_bool_t		 is_destroying; /* Destroy already called   */
     int			 af;		/* Address family	    */
     pj_stun_config	 stun_cfg;	/* STUN config (ioqueue etc)*/
@@ -59,5 +65,5 @@
     pj_uint16_t		 tsx_id[6];	/* .. to match STUN msg	    */
     pj_stun_session	*stun_sess;	/* STUN session		    */
-
+    pj_grp_lock_t	*grp_lock;	/* Session group lock	    */
 };
 
@@ -65,4 +71,7 @@
  * Prototypes for static functions 
  */
+
+/* Destructor for group lock */
+static void stun_sock_destructor(void *obj);
 
 /* This callback is called by the STUN session to send packet */
@@ -203,4 +212,18 @@
 	stun_sock->ka_interval = PJ_STUN_KEEP_ALIVE_SEC;
 
+    if (cfg && cfg->grp_lock) {
+	stun_sock->grp_lock = cfg->grp_lock;
+    } else {
+	status = pj_grp_lock_create(pool, NULL, &stun_sock->grp_lock);
+	if (status != PJ_SUCCESS) {
+	    pj_pool_release(pool);
+	    return status;
+	}
+    }
+
+    pj_grp_lock_add_ref(stun_sock->grp_lock);
+    pj_grp_lock_add_handler(stun_sock->grp_lock, pool, stun_sock,
+			    &stun_sock_destructor);
+
     /* Create socket and bind socket */
     status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &stun_sock->sock_fd);
@@ -253,4 +276,5 @@
 
 	pj_activesock_cfg_default(&activesock_cfg);
+	activesock_cfg.grp_lock = stun_sock->grp_lock;
 	activesock_cfg.async_cnt = cfg->async_cnt;
 	activesock_cfg.concurrency = 0;
@@ -291,4 +315,5 @@
 					stun_sock->obj_name,
 					&sess_cb, PJ_FALSE, 
+					stun_sock->grp_lock,
 					&stun_sock->stun_sess);
 	if (status != PJ_SUCCESS)
@@ -333,4 +358,6 @@
     PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL);
 
+    pj_grp_lock_acquire(stun_sock->grp_lock);
+
     /* Check whether the domain contains IP address */
     stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af;
@@ -361,5 +388,4 @@
 
 	/* Processing will resume when the DNS SRV callback is called */
-	return status;
 
     } else {
@@ -379,11 +405,16 @@
 
 	/* Start sending Binding request */
-	return get_mapped_addr(stun_sock);
-    }
-}
-
-/* Destroy */
-PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
-{
+	status = get_mapped_addr(stun_sock);
+    }
+
+    pj_grp_lock_release(stun_sock->grp_lock);
+    return status;
+}
+
+/* Destructor */
+static void stun_sock_destructor(void *obj)
+{
+    pj_stun_sock *stun_sock = (pj_stun_sock*)obj;
+
     if (stun_sock->q) {
 	pj_dns_srv_cancel_query(stun_sock->q, PJ_FALSE);
@@ -391,32 +422,10 @@
     }
 
-    if (stun_sock->stun_sess) {
-	pj_stun_session_set_user_data(stun_sock->stun_sess, NULL);
-    }
-    
-    /* Destroy the active socket first just in case we'll get
-     * stray callback.
-     */
-    if (stun_sock->active_sock != NULL) {
-	pj_activesock_t	*asock = stun_sock->active_sock;
-	stun_sock->active_sock = NULL;
-	stun_sock->sock_fd = PJ_INVALID_SOCKET;
-	pj_activesock_set_user_data(asock, NULL);
-	pj_activesock_close(asock);
-    } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
-	pj_sock_close(stun_sock->sock_fd);
-	stun_sock->sock_fd = PJ_INVALID_SOCKET;
-    }
-
-    if (stun_sock->ka_timer.id != 0) {
-	pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap, 
-			     &stun_sock->ka_timer);
-	stun_sock->ka_timer.id = 0;
-    }
-
+    /*
     if (stun_sock->stun_sess) {
 	pj_stun_session_destroy(stun_sock->stun_sess);
 	stun_sock->stun_sess = NULL;
     }
+    */
 
     if (stun_sock->pool) {
@@ -426,4 +435,38 @@
     }
 
+    TRACE_(("", "STUN sock %p destroyed", stun_sock));
+
+}
+
+/* Destroy */
+PJ_DEF(pj_status_t) pj_stun_sock_destroy(pj_stun_sock *stun_sock)
+{
+    TRACE_((stun_sock->obj_name, "STUN sock %p request, ref_cnt=%d",
+	    stun_sock, pj_grp_lock_get_ref(stun_sock->grp_lock)));
+
+    pj_grp_lock_acquire(stun_sock->grp_lock);
+    if (stun_sock->is_destroying) {
+	/* Destroy already called */
+	pj_grp_lock_release(stun_sock->grp_lock);
+	return PJ_EINVALIDOP;
+    }
+
+    stun_sock->is_destroying = PJ_TRUE;
+    pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+                                   &stun_sock->ka_timer, 0);
+
+    if (stun_sock->active_sock != NULL) {
+	stun_sock->sock_fd = PJ_INVALID_SOCKET;
+	pj_activesock_close(stun_sock->active_sock);
+    } else if (stun_sock->sock_fd != PJ_INVALID_SOCKET) {
+	pj_sock_close(stun_sock->sock_fd);
+	stun_sock->sock_fd = PJ_INVALID_SOCKET;
+    }
+
+    if (stun_sock->stun_sess) {
+	pj_stun_session_destroy(stun_sock->stun_sess);
+    }
+    pj_grp_lock_dec_ref(stun_sock->grp_lock);
+    pj_grp_lock_release(stun_sock->grp_lock);
     return PJ_SUCCESS;
 }
@@ -469,4 +512,6 @@
     pj_stun_sock *stun_sock = (pj_stun_sock*) user_data;
 
+    pj_grp_lock_acquire(stun_sock->grp_lock);
+
     /* Clear query */
     stun_sock->q = NULL;
@@ -475,4 +520,5 @@
     if (status != PJ_SUCCESS) {
 	sess_fail(stun_sock, PJ_STUN_SOCK_DNS_OP, status);
+	pj_grp_lock_release(stun_sock->grp_lock);
 	return;
     }
@@ -491,4 +537,6 @@
     /* Start sending Binding request */
     get_mapped_addr(stun_sock);
+
+    pj_grp_lock_release(stun_sock->grp_lock);
 }
 
@@ -534,4 +582,6 @@
     PJ_ASSERT_RETURN(stun_sock && info, PJ_EINVAL);
 
+    pj_grp_lock_acquire(stun_sock->grp_lock);
+
     /* Copy STUN server address and mapped address */
     pj_memcpy(&info->srv_addr, &stun_sock->srv_addr,
@@ -544,6 +594,8 @@
     status = pj_sock_getsockname(stun_sock->sock_fd, &info->bound_addr,
 				 &addr_len);
-    if (status != PJ_SUCCESS)
+    if (status != PJ_SUCCESS) {
+	pj_grp_lock_release(stun_sock->grp_lock);
 	return status;
+    }
 
     /* If socket is bound to a specific interface, then only put that
@@ -561,6 +613,8 @@
 	/* Get the default address */
 	status = pj_gethostip(stun_sock->af, &def_addr);
-	if (status != PJ_SUCCESS)
+	if (status != PJ_SUCCESS) {
+	    pj_grp_lock_release(stun_sock->grp_lock);
 	    return status;
+	}
 	
 	pj_sockaddr_set_port(&def_addr, port);
@@ -570,6 +624,8 @@
 	status = pj_enum_ip_interface(stun_sock->af, &info->alias_cnt, 
 				      info->aliases);
-	if (status != PJ_SUCCESS)
+	if (status != PJ_SUCCESS) {
+	    pj_grp_lock_release(stun_sock->grp_lock);
 	    return status;
+	}
 
 	/* Set the port number for each address.
@@ -591,4 +647,5 @@
     }
 
+    pj_grp_lock_release(stun_sock->grp_lock);
     return PJ_SUCCESS;
 }
@@ -604,12 +661,27 @@
 {
     pj_ssize_t size;
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(stun_sock && pkt && dst_addr && addr_len, PJ_EINVAL);
     
+    pj_grp_lock_acquire(stun_sock->grp_lock);
+
+    if (!stun_sock->active_sock) {
+	/* We have been shutdown, but this callback may still get called
+	 * by retransmit timer.
+	 */
+	pj_grp_lock_release(stun_sock->grp_lock);
+	return PJ_EINVALIDOP;
+    }
+
     if (send_key==NULL)
 	send_key = &stun_sock->send_key;
 
     size = pkt_len;
-    return pj_activesock_sendto(stun_sock->active_sock, send_key, 
-				pkt, &size, flag, dst_addr, addr_len);
+    status = pj_activesock_sendto(stun_sock->active_sock, send_key,
+                                  pkt, &size, flag, dst_addr, addr_len);
+
+    pj_grp_lock_release(stun_sock->grp_lock);
+    return status;
 }
 
@@ -626,6 +698,10 @@
 
     stun_sock = (pj_stun_sock *) pj_stun_session_get_user_data(sess);
-    if (!stun_sock || !stun_sock->active_sock)
+    if (!stun_sock || !stun_sock->active_sock) {
+	/* We have been shutdown, but this callback may still get called
+	 * by retransmit timer.
+	 */
 	return PJ_EINVALIDOP;
+    }
 
     pj_assert(token==INTERNAL_MSG_TOKEN);
@@ -633,5 +709,5 @@
 
     size = pkt_size;
-    return pj_activesock_sendto(stun_sock->active_sock, 
+    return pj_activesock_sendto(stun_sock->active_sock,
 				&stun_sock->int_send_key,
 				pkt, &size, 0, dst_addr, addr_len);
@@ -727,12 +803,9 @@
 static void start_ka_timer(pj_stun_sock *stun_sock)
 {
-    if (stun_sock->ka_timer.id != 0) {
-	pj_timer_heap_cancel(stun_sock->stun_cfg.timer_heap, 
-			     &stun_sock->ka_timer);
-	stun_sock->ka_timer.id = 0;
-    }
+    pj_timer_heap_cancel_if_active(stun_sock->stun_cfg.timer_heap,
+                                   &stun_sock->ka_timer, 0);
 
     pj_assert(stun_sock->ka_interval != 0);
-    if (stun_sock->ka_interval > 0) {
+    if (stun_sock->ka_interval > 0 && !stun_sock->is_destroying) {
 	pj_time_val delay;
 
@@ -740,10 +813,8 @@
 	delay.msec = 0;
 
-	if (pj_timer_heap_schedule(stun_sock->stun_cfg.timer_heap, 
-				   &stun_sock->ka_timer, 
-				   &delay) == PJ_SUCCESS)
-	{
-	    stun_sock->ka_timer.id = PJ_TRUE;
-	}
+	pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap,
+	                                  &stun_sock->ka_timer,
+	                                  &delay, PJ_TRUE,
+	                                  stun_sock->grp_lock);
     }
 }
@@ -757,12 +828,16 @@
 
     PJ_UNUSED_ARG(th);
+    pj_grp_lock_acquire(stun_sock->grp_lock);
 
     /* Time to send STUN Binding request */
-    if (get_mapped_addr(stun_sock) != PJ_SUCCESS)
+    if (get_mapped_addr(stun_sock) != PJ_SUCCESS) {
+	pj_grp_lock_release(stun_sock->grp_lock);
 	return;
+    }
 
     /* Next keep-alive timer will be scheduled once the request
      * is complete.
      */
+    pj_grp_lock_release(stun_sock->grp_lock);
 }
 
@@ -788,4 +863,6 @@
 	return PJ_TRUE;
     }
+
+    pj_grp_lock_acquire(stun_sock->grp_lock);
 
     /* Check that this is STUN message */
@@ -824,5 +901,8 @@
 				       PJ_STUN_IS_DATAGRAM, NULL, NULL,
 				       src_addr, addr_len);
-    return status!=PJNATH_ESTUNDESTROYED ? PJ_TRUE : PJ_FALSE;
+
+    status = pj_grp_lock_release(stun_sock->grp_lock);
+
+    return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
 
 process_app_data:
@@ -832,8 +912,10 @@
 	ret = (*stun_sock->cb.on_rx_data)(stun_sock, data, size,
 					  src_addr, addr_len);
-	return ret;
-    }
-
-    return PJ_TRUE;
+	status = pj_grp_lock_release(stun_sock->grp_lock);
+	return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
+    }
+
+    status = pj_grp_lock_release(stun_sock->grp_lock);
+    return status!=PJ_EGONE ? PJ_TRUE : PJ_FALSE;
 }
 
@@ -857,4 +939,6 @@
     if (stun_sock->cb.on_data_sent) {
 	pj_bool_t ret;
+
+	pj_grp_lock_acquire(stun_sock->grp_lock);
 
 	/* If app gives NULL send_key in sendto() function, then give
@@ -867,4 +951,5 @@
 	ret = (*stun_sock->cb.on_data_sent)(stun_sock, send_key, sent);
 
+	pj_grp_lock_release(stun_sock->grp_lock);
 	return ret;
     }
