Ticket #970: More gracefull PJSUA-LIB shutdown sequence. Enhancements:
 - wait for unregistration to complete (or a preconfigured delay expires)
 - new account config field to set the maximum delay to wait for unregistration
 - rejects incoming requests (INVITE, SUBSCRIBE, and OPTIONS) when shutdown is in progress


git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2943 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index d6db65b..c0eb8b2 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1725,6 +1725,17 @@
 
 
 /**
+ * Default maximum time to wait for account unregistration transactions to
+ * complete during library shutdown sequence.
+ *
+ * Default: 4000 (4 seconds)
+ */
+#ifndef PJSUA_UNREG_TIMEOUT
+#   define PJSUA_UNREG_TIMEOUT	    4000
+#endif
+
+
+/**
  * Default PUBLISH expiration
  */
 #ifndef PJSUA_PUBLISH_EXPIRATION
@@ -1919,6 +1930,14 @@
      */
     unsigned	    reg_timeout;
 
+    /**
+     * Specify the maximum time to wait for unregistration requests to
+     * complete during library shutdown sequence.
+     *
+     * Default: PJSUA_UNREG_TIMEOUT
+     */
+    unsigned	    unreg_timeout;
+
     /** 
      * Number of credentials in the credential array.
      */
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index c57873f..a845940 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -645,6 +645,14 @@
     if (dlg || tsx)
 	return PJ_FALSE;
 
+    /* Don't want to accept the call if shutdown is in progress */
+    if (pjsua_var.thread_quit_flag) {
+	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 
+				      PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
+				      NULL, NULL);
+	return PJ_TRUE;
+    }
+
     PJSUA_LOCK();
 
     /* Find free call slot. */
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 39ad80d..0b6f48c 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -161,6 +161,7 @@
     pj_bzero(cfg, sizeof(*cfg));
 
     cfg->reg_timeout = PJSUA_REG_INTERVAL;
+    cfg->unreg_timeout = PJSUA_UNREG_TIMEOUT;
     pjsip_publishc_opt_default(&cfg->publish_opt);
     cfg->unpublish_max_wait_time_msec = PJSUA_UNPUBLISH_MAX_WAIT_TIME_MSEC;
     cfg->transport_id = PJSUA_INVALID_ID;
@@ -305,6 +306,14 @@
 	return PJ_FALSE;
     }
 
+    /* Don't want to handle if shutdown is in progress */
+    if (pjsua_var.thread_quit_flag) {
+	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 
+				      PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
+				      NULL, NULL);
+	return PJ_TRUE;
+    }
+
     /* Create basic response. */
     status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, 
 					 &tdata);
@@ -1231,6 +1240,8 @@
     if (pjsua_var.endpt) {
 	unsigned max_wait;
 
+	PJ_LOG(4,(THIS_FILE, "Shutting down..."));
+
 	/* Terminate all calls. */
 	pjsua_call_hangup_all();
 
@@ -1245,6 +1256,9 @@
 	/* Terminate all presence subscriptions. */
 	pjsua_pres_shutdown();
 
+	/* Destroy media (to shutdown media transports etc) */
+	pjsua_media_subsys_destroy();
+
 	/* Wait for sometime until all publish client sessions are done
 	 * (ticket #364)
 	 */
@@ -1290,10 +1304,6 @@
 		pjsua_acc_set_registration(i, PJ_FALSE);
 	    }
 	}
-    }
-
-    /* Destroy endpoint. */
-    if (pjsua_var.endpt) {
 
 	/* Terminate any pending STUN resolution */
 	if (!pj_list_empty(&pjsua_var.stun_res)) {
@@ -1305,24 +1315,41 @@
 	    }
 	}
 
+	/* Wait until all unregistrations are done (ticket #364) */
+	/* First stage, get the maximum wait time */
+	max_wait = 100;
+	for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	    if (!pjsua_var.acc[i].valid)
+		continue;
+	    if (pjsua_var.acc[i].cfg.unreg_timeout > max_wait)
+		max_wait = pjsua_var.acc[i].cfg.unreg_timeout;
+	}
+	
+	/* Second stage, wait for unregistrations to complete */
+	for (i=0; i<(int)(max_wait/50); ++i) {
+	    unsigned j;
+	    for (j=0; j<PJ_ARRAY_SIZE(pjsua_var.acc); ++j) {
+		if (!pjsua_var.acc[j].valid)
+		    continue;
+
+		if (pjsua_var.acc[j].regc)
+		    break;
+	    }
+	    if (j != PJ_ARRAY_SIZE(pjsua_var.acc))
+		busy_sleep(50);
+	    else
+		break;
+	}
+	/* Note variable 'i' is used below */
+
 	/* Wait for some time to allow unregistration and ICE/TURN
 	 * transports shutdown to complete: 
-	*/
-	PJ_LOG(4,(THIS_FILE, "Shutting down..."));
-	busy_sleep(1000);
+	 */
+	if (i < 20)
+	    busy_sleep(1000 - i*50);
 
 	PJ_LOG(4,(THIS_FILE, "Destroying..."));
 
-	/* Terminate all calls again, just in case there's new call
-	 * picked up during busy_sleep()
-	 */
-	pjsua_call_hangup_all();
-
-	/* Destroy media after all polling is done, as there may be
-	 * incoming request that needs handling (e.g. OPTIONS)
-	 */
-	pjsua_media_subsys_destroy();
-
 	/* Must destroy endpoint first before destroying pools in
 	 * buddies or accounts, since shutting down transaction layer
 	 * may emit events which trigger some buddy or account callbacks
@@ -1346,9 +1373,6 @@
 		pjsua_var.acc[i].pool = NULL;
 	    }
 	}
-    } else {
-	/* Destroy media */
-	pjsua_media_subsys_destroy();
     }
 
     /* Destroy mutex */
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 243f4f6..01f33a1 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -640,6 +640,8 @@
 {
     unsigned i;
 
+    PJ_LOG(4,(THIS_FILE, "Shutting down media.."));
+
     close_snd_dev();
 
     if (pjsua_var.mconf) {
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index 4515636..935a41e 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -682,6 +682,14 @@
 
     /* Incoming SUBSCRIBE: */
 
+    /* Don't want to accept the request if shutdown is in progress */
+    if (pjsua_var.thread_quit_flag) {
+	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 
+				      PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
+				      NULL, NULL);
+	return PJ_TRUE;
+    }
+
     PJSUA_LOCK();
 
     /* Find which account for the incoming request. */
@@ -1721,6 +1729,8 @@
 {
     unsigned i;
 
+    PJ_LOG(4,(THIS_FILE, "Shutting down presence.."));
+
     if (pjsua_var.pres_timer.id != 0) {
 	pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.pres_timer);
 	pjsua_var.pres_timer.id = PJ_FALSE;