Implementation of Re #1628: Modify SIP transaction to use group lock to avoid deadlock etc.


git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@4420 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/src/test/regc_test.c b/pjsip/src/test/regc_test.c
index 7a5b806..17cb399 100644
--- a/pjsip/src/test/regc_test.c
+++ b/pjsip/src/test/regc_test.c
@@ -347,7 +347,10 @@
 		  client_cfg->error, client_result.error));
 	return -210;
     }
-    if (client_result.code != client_cfg->code) {
+    if (client_result.code != client_cfg->code &&
+	client_cfg->code != 502 && client_cfg->code != 503 &&
+	client_result.code != 502 && client_result.code != 503)
+    {
 	PJ_LOG(3,(THIS_FILE, "    error: expecting code=%d, got code=%d",
 		  client_cfg->code, client_result.code));
 	return -220;
diff --git a/pjsip/src/test/test.c b/pjsip/src/test/test.c
index 8ffe566..0361e61 100644
--- a/pjsip/src/test/test.c
+++ b/pjsip/src/test/test.c
@@ -47,9 +47,10 @@
 
 
 pjsip_endpoint *endpt;
+pj_caching_pool caching_pool;
 int log_level = 3;
 int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | 
-		      PJ_LOG_HAS_MICRO_SEC;
+		      PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_INDENT;
 
 static pj_oshandle_t fd_report;
 const char *system_name = "Unknown";
@@ -223,7 +224,6 @@
 int test_main(void)
 {
     pj_status_t rc;
-    pj_caching_pool caching_pool;
     const char *filename;
     unsigned tsx_test_cnt=0;
     struct tsx_test_param tsx_test[10];
@@ -369,6 +369,12 @@
     DO_TEST(regc_test());
 #endif
 
+    /*
+     * Better be last because it recreates the endpt
+     */
+#if INCLUDE_TSX_DESTROY_TEST
+    DO_TEST(tsx_destroy_test());
+#endif
 
 on_return:
     flush_events(500);
diff --git a/pjsip/src/test/test.h b/pjsip/src/test/test.h
index 358f930..690598b 100644
--- a/pjsip/src/test/test.h
+++ b/pjsip/src/test/test.h
@@ -23,6 +23,7 @@
 #include <pjsip/sip_types.h>
 
 extern pjsip_endpoint *endpt;
+extern pj_caching_pool caching_pool;
 
 #define TEST_UDP_PORT	    15060
 #define TEST_UDP_PORT_STR   "15060"
@@ -64,6 +65,7 @@
 #define INCLUDE_TCP_TEST	INCLUDE_TRANSPORT_GROUP
 #define INCLUDE_RESOLVE_TEST	INCLUDE_TRANSPORT_GROUP
 #define INCLUDE_TSX_TEST	INCLUDE_TSX_GROUP
+#define INCLUDE_TSX_DESTROY_TEST INCLUDE_TSX_GROUP
 #define INCLUDE_INV_OA_TEST	INCLUDE_INV_GROUP
 #define INCLUDE_REGC_TEST	INCLUDE_REGC_GROUP
 
@@ -75,6 +77,7 @@
 int multipart_test(void);
 int txdata_test(void);
 int tsx_bench(void);
+int tsx_destroy_test(void);
 int transport_udp_test(void);
 int transport_loop_test(void);
 int transport_tcp_test(void);
diff --git a/pjsip/src/test/tsx_basic_test.c b/pjsip/src/test/tsx_basic_test.c
index 163c3b9..2de73d7 100644
--- a/pjsip/src/test/tsx_basic_test.c
+++ b/pjsip/src/test/tsx_basic_test.c
@@ -125,7 +125,7 @@
 	    app_perror("   error: unable to terminate transaction", status);
 	    return -40;
 	}
-	pj_mutex_unlock(tsx->mutex);
+	pj_grp_lock_release(tsx->grp_lock);
     }
 
     flush_events(500);
@@ -155,3 +155,189 @@
 
     return 0;
 }
+
+/**************************************************************************/
+
+struct tsx_test_state
+{
+    int pool_cnt;
+};
+
+static void save_tsx_test_state(struct tsx_test_state *st)
+{
+    st->pool_cnt = caching_pool.used_count;
+}
+
+static pj_status_t check_tsx_test_state(struct tsx_test_state *st)
+{
+    if (caching_pool.used_count > st->pool_cnt)
+	return -1;
+
+    return 0;
+}
+
+static void destroy_endpt()
+{
+    pjsip_endpt_destroy(endpt);
+    endpt = NULL;
+}
+
+static pj_status_t init_endpt()
+{
+    pj_str_t ns = { "10.187.27.172", 13};	/* just a random, unreachable IP */
+    pj_dns_resolver *resolver;
+    pj_status_t rc;
+
+    rc = pjsip_endpt_create(&caching_pool.factory, "endpt", &endpt);
+    if (rc != PJ_SUCCESS) {
+	app_perror("pjsip_endpt_create", rc);
+	return rc;
+    }
+
+    /* Start transaction layer module. */
+    rc = pjsip_tsx_layer_init_module(endpt);
+    if (rc != PJ_SUCCESS) {
+	app_perror("tsx_layer_init", rc);
+	return rc;
+    }
+
+    rc = pjsip_udp_transport_start(endpt, NULL, NULL, 1,  NULL);
+    if (rc != PJ_SUCCESS) {
+	app_perror("udp init", rc);
+	return rc;
+    }
+
+    rc = pjsip_tcp_transport_start(endpt, NULL, 1, NULL);
+    if (rc != PJ_SUCCESS) {
+	app_perror("tcp init", rc);
+	return rc;
+    }
+
+    rc = pjsip_endpt_create_resolver(endpt, &resolver);
+    if (rc != PJ_SUCCESS) {
+	app_perror("create resolver", rc);
+	return rc;
+    }
+
+    pj_dns_resolver_set_ns(resolver, 1, &ns, NULL);
+
+    rc = pjsip_endpt_set_resolver(endpt, resolver);
+    if (rc != PJ_SUCCESS) {
+	app_perror("set resolver", rc);
+	return rc;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static int tsx_create_and_send_req(void *arg)
+{
+    pj_str_t dst_uri = pj_str((char*)arg);
+    pj_str_t from_uri = pj_str((char*)"<sip:user@host>");
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    status = pjsip_endpt_create_request(endpt, &pjsip_options_method,
+                                        &dst_uri, &from_uri, &dst_uri,
+                                        NULL, NULL, -1, NULL,
+                                        &tdata);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+int tsx_destroy_test()
+{
+    struct tsx_test_state state;
+    struct test_desc
+    {
+	const char *title;
+	int (*func)(void*);
+	void *arg;
+	int sleep_before_unload;
+	int sleep_after_unload;
+    } test_entries[] =
+    {
+	{
+	    "normal unable to resolve",
+	    &tsx_create_and_send_req,
+	    "sip:user@somehost",
+	    10000,
+	    1
+	},
+	{
+	    "resolve and destroy, wait",
+	    &tsx_create_and_send_req,
+	    "sip:user@somehost",
+	    1,
+	    10000
+	},
+	{
+	    "tcp connect and destroy",
+	    &tsx_create_and_send_req,
+	    "sip:user@10.125.36.63:58517;transport=tcp",
+	    60000,
+	    1000
+	},
+	{
+	    "tcp connect and destroy",
+	    &tsx_create_and_send_req,
+	    "sip:user@10.125.36.63:58517;transport=tcp",
+	    1,
+	    60000
+	},
+
+    };
+    int rc;
+    unsigned i;
+    const int INDENT = 2;
+
+    pj_log_add_indent(INDENT);
+    destroy_endpt();
+
+    for (i=0; i<PJ_ARRAY_SIZE(test_entries); ++i) {
+	struct test_desc *td = &test_entries[i];
+
+	PJ_LOG(3,(THIS_FILE, "%s", td->title));
+
+	pj_log_add_indent(INDENT);
+	save_tsx_test_state(&state);
+
+	rc = init_endpt();
+	if (rc != PJ_SUCCESS) {
+	    pj_log_add_indent(-INDENT*2);
+	    return -10;
+	}
+
+	rc = td->func(td->arg);
+	if (rc != PJ_SUCCESS) {
+	    pj_log_add_indent(-INDENT*2);
+	    return -20;
+	}
+
+	flush_events(td->sleep_before_unload);
+	pjsip_tsx_layer_destroy();
+	flush_events(td->sleep_after_unload);
+	destroy_endpt();
+
+	rc = check_tsx_test_state(&state);
+	if (rc != PJ_SUCCESS) {
+	    init_endpt();
+	    pj_log_add_indent(-INDENT*2);
+	    return -30;
+	}
+
+	pj_log_add_indent(-INDENT);
+    }
+
+    init_endpt();
+
+    pj_log_add_indent(-INDENT);
+    return 0;
+}
+
diff --git a/pjsip/src/test/tsx_bench.c b/pjsip/src/test/tsx_bench.c
index 8ae5778..2282b6a 100644
--- a/pjsip/src/test/tsx_bench.c
+++ b/pjsip/src/test/tsx_bench.c
@@ -79,8 +79,13 @@
 on_error:
     for (i=0; i<working_set; ++i) {
 	if (tsx[i]) {
+	    pj_timer_heap_t *th;
+
 	    pjsip_tsx_terminate(tsx[i], 601);
 	    tsx[i] = NULL;
+
+	    th = pjsip_endpt_get_timer_heap(endpt);
+	    pj_timer_heap_poll(th, NULL);
 	}
     }
     pjsip_tx_data_dec_ref(request);
@@ -177,8 +182,14 @@
 on_error:
     for (i=0; i<working_set; ++i) {
 	if (tsx[i]) {
+	    pj_timer_heap_t *th;
+
 	    pjsip_tsx_terminate(tsx[i], 601);
 	    tsx[i] = NULL;
+
+	    th = pjsip_endpt_get_timer_heap(endpt);
+	    pj_timer_heap_poll(th, NULL);
+
 	}
     }
     pjsip_tx_data_dec_ref(request);
@@ -208,6 +219,8 @@
     for (i=0; i<REPEAT; ++i) {
 	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
 		  i+1, REPEAT));
+	PJ_LOG(3,(THIS_FILE, "    number of current tsx: %d",
+		  pjsip_tsx_layer_get_tsx_count()));
 	status = uac_tsx_bench(WORKING_SET, &usec[i]);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -246,6 +259,8 @@
     for (i=0; i<REPEAT; ++i) {
 	PJ_LOG(3,(THIS_FILE, "    test %d of %d..",
 		  i+1, REPEAT));
+	PJ_LOG(3,(THIS_FILE, "    number of current tsx: %d",
+		  pjsip_tsx_layer_get_tsx_count()));
 	status = uas_tsx_bench(WORKING_SET, &usec[i]);
 	if (status != PJ_SUCCESS)
 	    return status;
diff --git a/pjsip/src/test/tsx_uac_test.c b/pjsip/src/test/tsx_uac_test.c
index 0677da1..d978df8 100644
--- a/pjsip/src/test/tsx_uac_test.c
+++ b/pjsip/src/test/tsx_uac_test.c
@@ -220,10 +220,13 @@
 	if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
 
 	    /* Test the status code. */
-	    if (tsx->status_code != PJSIP_SC_TSX_TRANSPORT_ERROR) {
+	    if (tsx->status_code != PJSIP_SC_TSX_TRANSPORT_ERROR &&
+		tsx->status_code != PJSIP_SC_BAD_GATEWAY)
+	    {
 		PJ_LOG(3,(THIS_FILE, 
-			  "    error: status code is %d instead of %d",
-			  tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR));
+			  "    error: status code is %d instead of %d or %d",
+			  tsx->status_code, PJSIP_SC_TSX_TRANSPORT_ERROR,
+			  PJSIP_SC_BAD_GATEWAY));
 		test_complete = -720;
 	    }
 
@@ -688,7 +691,7 @@
 	    tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE);
 	    if (tsx) {
 		pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED);
-		pj_mutex_unlock(tsx->mutex);
+		pj_grp_lock_release(tsx->grp_lock);
 	    } else {
 		PJ_LOG(3,(THIS_FILE, "    error: uac transaction not found!"));
 		test_complete = -633;
@@ -1027,7 +1030,7 @@
 	tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
 	if (tsx) {
 	    pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED);
-	    pj_mutex_unlock(tsx->mutex);
+	    pj_grp_lock_release(tsx->grp_lock);
 	    flush_events(1000);
 	}
 	pjsip_tx_data_dec_ref(tdata);
diff --git a/pjsip/src/test/tsx_uas_test.c b/pjsip/src/test/tsx_uas_test.c
index 973f331..3c43f53 100644
--- a/pjsip/src/test/tsx_uas_test.c
+++ b/pjsip/src/test/tsx_uas_test.c
@@ -225,12 +225,12 @@
     if (status != PJ_SUCCESS) {
 	// Some tests do expect failure!
 	//PJ_LOG(3,(THIS_FILE,"    error: timer unable to send response"));
-	pj_mutex_unlock(tsx->mutex);
+	pj_grp_lock_release(tsx->grp_lock);
 	pjsip_tx_data_dec_ref(r->tdata);
 	return;
     }
 
-    pj_mutex_unlock(tsx->mutex);
+    pj_grp_lock_release(tsx->grp_lock);
 }
 
 /* Utility to send response. */
@@ -313,7 +313,7 @@
     }
 
     pjsip_tsx_terminate(tsx, status_code);
-    pj_mutex_unlock(tsx->mutex);
+    pj_grp_lock_release(tsx->grp_lock);
 }
 
 #if 0	/* Unused for now */
@@ -1259,7 +1259,7 @@
 	tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
 	if (tsx) {
 	    pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED);
-	    pj_mutex_unlock(tsx->mutex);
+	    pj_grp_lock_release(tsx->grp_lock);
 	    flush_events(1000);
 	}
 	pjsip_tx_data_dec_ref(tdata);