From 83da7bd3188ddcb92b10ddc4e07792c40aa4885f Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Thu, 16 Sep 2010 17:01:53 -0500
Subject: [PATCH] make t38 terminal mode more reliable

---
 .../mod_spandsp/mod_spandsp_fax.c             | 172 +++++++++++-------
 1 file changed, 102 insertions(+), 70 deletions(-)

diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c
index 52ba941d43..1c825ec8dd 100644
--- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c
+++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c
@@ -74,6 +74,8 @@ static struct {
 	char header[50];
 	char *prepend_string;
 	char *spool;
+	switch_thread_cond_t *cond;
+	switch_mutex_t *cond_mutex;    
 } globals;
 
 struct pvt_s {
@@ -121,24 +123,16 @@ static struct {
 static int add_pvt(pvt_t *pvt)
 {
     int r = 0;
-    uint32_t sanity = 50;
-
-    switch_mutex_lock(t38_state_list.mutex);
-    if (!t38_state_list.thread_running) {
-
-        launch_timer_thread();
-
-        while(--sanity && !t38_state_list.thread_running) {
-            switch_yield(10000);
-        }
-    }
-    switch_mutex_unlock(t38_state_list.mutex);
     
     if (t38_state_list.thread_running) {
         switch_mutex_lock(t38_state_list.mutex);
         pvt->next = t38_state_list.head;
         t38_state_list.head = pvt;
         switch_mutex_unlock(t38_state_list.mutex);
+        r = 1;
+        switch_thread_cond_broadcast(globals.cond);
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error launching thread\n");
     }
 
     return r;
@@ -151,9 +145,9 @@ static int del_pvt(pvt_t *del_pvt)
     pvt_t *p, *l = NULL;
     int r = 0;
 
-    if (!t38_state_list.thread_running) goto end;
-    
+
     switch_mutex_lock(t38_state_list.mutex);
+
     for (p = t38_state_list.head; p; p = p->next) {
         if (p == del_pvt) {
             if (l) {
@@ -163,42 +157,48 @@ static int del_pvt(pvt_t *del_pvt)
             }
             p->next = NULL;
             r = 1;
-            goto end;
+            break;
         }
 
         l = p;
     }
 
- end:
-
     switch_mutex_unlock(t38_state_list.mutex);
 
-    return r;
+    switch_thread_cond_broadcast(globals.cond);
 
+    return r;
 }
 
 static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *obj)
 {
     switch_timer_t timer = { 0 };
     pvt_t *pvt;
-    int samples = 240;
-    int ms = 30;
+    int samples = 160;
+    int ms = 20;
 
-    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "timer thread started.\n");
+    switch_mutex_lock(t38_state_list.mutex);
+    t38_state_list.thread_running = 1;
+    switch_mutex_unlock(t38_state_list.mutex);
+
+    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FAX timer thread started.\n");
 
 	if (switch_core_timer_init(&timer, "soft", ms, samples, NULL) != SWITCH_STATUS_SUCCESS) {
-        return NULL;
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "timer init failed.\n");
+        goto end;
     }
 
-    t38_state_list.thread_running = 1;
-
     while(t38_state_list.thread_running) {
 
+		switch_mutex_lock(globals.cond_mutex);
+
         switch_mutex_lock(t38_state_list.mutex);
 
         if (!t38_state_list.head) {
             switch_mutex_unlock(t38_state_list.mutex);
-            goto end;
+			switch_thread_cond_wait(globals.cond, globals.cond_mutex);
+            switch_core_timer_sync(&timer);
+            continue;
         }
 
         for (pvt = t38_state_list.head; pvt; pvt = pvt->next) {
@@ -214,10 +214,15 @@ static void *SWITCH_THREAD_FUNC timer_thread_run(switch_thread_t *thread, void *
     
  end:
 
-    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "timer thread ended.\n");
-
+    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "FAX timer thread ended.\n");
+    
+    switch_mutex_lock(t38_state_list.mutex);
     t38_state_list.thread_running = 0;
-    switch_core_timer_destroy(&timer);
+    switch_mutex_unlock(t38_state_list.mutex);
+
+    if (timer.timer_interface) {
+        switch_core_timer_destroy(&timer);
+    }
     
     return NULL;
 }
@@ -468,49 +473,61 @@ static switch_status_t spanfax_init(pvt_t *pvt, transport_mode_t trans_mode)
 		}
 		break;
 	case T38_MODE:
-		if (pvt->t38_state == NULL) {
-			pvt->t38_state = (t38_terminal_state_t *) switch_core_session_alloc(pvt->session, sizeof(t38_terminal_state_t));
-		}
-		if (pvt->t38_state == NULL) {
-			return SWITCH_STATUS_FALSE;
-		}
-		if (pvt->udptl_state == NULL) {
-            pvt->udptl_state = (udptl_state_t *) switch_core_session_alloc(pvt->session, sizeof(udptl_state_t));
-        }
-		if (pvt->udptl_state == NULL) {
-    		t38_terminal_free(pvt->t38_state);
-            pvt->t38_state = NULL;
-			return SWITCH_STATUS_FALSE;
-		}
+        {
+            switch_core_session_message_t msg = { 0 };
 
-        /* add to timer thread processing */
-        add_pvt(pvt);
+            if (pvt->t38_state == NULL) {
+                pvt->t38_state = (t38_terminal_state_t *) switch_core_session_alloc(pvt->session, sizeof(t38_terminal_state_t));
+            }
+            if (pvt->t38_state == NULL) {
+                return SWITCH_STATUS_FALSE;
+            }
+            if (pvt->udptl_state == NULL) {
+                pvt->udptl_state = (udptl_state_t *) switch_core_session_alloc(pvt->session, sizeof(udptl_state_t));
+            }
+            if (pvt->udptl_state == NULL) {
+                t38_terminal_free(pvt->t38_state);
+                pvt->t38_state = NULL;
+                return SWITCH_STATUS_FALSE;
+            }
+
+            t38 = pvt->t38_state;
+            t30 = t38_terminal_get_t30_state(t38);
+
+            memset(t38, 0, sizeof(t38_terminal_state_t));
+
+            if (t38_terminal_init(t38, pvt->caller, t38_tx_packet_handler, pvt) == NULL) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my T.38 structs\n");
+                return SWITCH_STATUS_FALSE;
+            }
+
+            pvt->t38_core = t38_terminal_get_t38_core_state(pvt->t38_state);
+
+            if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, 
+                           (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n");
+                return SWITCH_STATUS_FALSE;
+            }
+
+            msg.from = __FILE__;
+            msg.message_id = SWITCH_MESSAGE_INDICATE_UDPTL_MODE;
+            switch_core_session_receive_message(pvt->session, &msg);
         
-		t38 = pvt->t38_state;
-		t30 = t38_terminal_get_t30_state(t38);
+            /* add to timer thread processing */
+            if (!add_pvt(pvt)) {
+                if (channel) {
+                    switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                }
+            }
+        
+            span_log_set_message_handler(&t38->logging, spanfax_log_message);
+            span_log_set_message_handler(&t30->logging, spanfax_log_message);
 
-		memset(t38, 0, sizeof(t38_terminal_state_t));
-
-		if (t38_terminal_init(t38, pvt->caller, t38_tx_packet_handler, pvt) == NULL) {
-			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my T.38 structs\n");
-			return SWITCH_STATUS_FALSE;
-		}
-
-        pvt->t38_core = t38_terminal_get_t38_core_state(pvt->t38_state);
-
-        if (udptl_init(pvt->udptl_state, UDPTL_ERROR_CORRECTION_REDUNDANCY, 3, 3, 
-                       (udptl_rx_packet_handler_t *) t38_core_rx_ifp_packet, (void *) pvt->t38_core) == NULL) {
-			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot initialize my UDPTL structs\n");
-			return SWITCH_STATUS_FALSE;
-		}
-
-		span_log_set_message_handler(&t38->logging, spanfax_log_message);
-		span_log_set_message_handler(&t30->logging, spanfax_log_message);
-
-		if (pvt->verbose) {
-			span_log_set_level(&t38->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
-			span_log_set_level(&t30->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
-		}
+            if (pvt->verbose) {
+                span_log_set_level(&t38->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+                span_log_set_level(&t30->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+            }
+        }
 		break;
  case T38_GATEWAY_MODE:
 	 if (pvt->t38_gateway_state == NULL) {
@@ -1145,8 +1162,6 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat
                     //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "READ %d udptl bytes\n", read_frame->packetlen);
                     
                     udptl_rx_packet(pvt->udptl_state, read_frame->packet, read_frame->packetlen);
-
-
                 }
             }
             continue;
@@ -1282,6 +1297,8 @@ void mod_spandsp_fax_event_handler(switch_event_t *event)
 
 void mod_spandsp_fax_load(switch_memory_pool_t *pool)
 {
+    uint32_t sanity = 200;
+
 	memset(&globals, 0, sizeof(globals));
     memset(&t38_state_list, 0, sizeof(t38_state_list));
 
@@ -1289,6 +1306,9 @@ void mod_spandsp_fax_load(switch_memory_pool_t *pool)
 
 	switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
 	switch_mutex_init(&t38_state_list.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+
+	switch_mutex_init(&globals.cond_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+	switch_thread_cond_create(&globals.cond, globals.pool);
     
     globals.enable_t38 = 1;
 	globals.total_sessions = 0;
@@ -1301,10 +1321,22 @@ void mod_spandsp_fax_load(switch_memory_pool_t *pool)
 	strncpy(globals.header, "SpanDSP Fax Header", sizeof(globals.header) - 1);
 
 	load_configuration(0);
+
+
+    launch_timer_thread();
+
+    while(--sanity && !t38_state_list.thread_running) {
+        switch_yield(20000);
+    }
 }
 
 void mod_spandsp_fax_shutdown(void)
 {
+    switch_status_t tstatus = SWITCH_STATUS_SUCCESS;
+
+    t38_state_list.thread_running = 0;
+    switch_thread_cond_broadcast(globals.cond);
+    switch_thread_join(&tstatus, t38_state_list.thread);
 	memset(&globals, 0, sizeof(globals));
 }