[Freeswitch-svn] [commit] r13721 - freeswitch/trunk/src/mod/languages/mod_python

FreeSWITCH SVN mikej at freeswitch.org
Tue Jun 9 08:09:39 PDT 2009


Author: mikej
Date: Tue Jun  9 10:09:39 2009
New Revision: 13721

Log:
mod_python: fix seg on unload when scripts are running (MODLANG-107)

Modified:
   freeswitch/trunk/src/mod/languages/mod_python/mod_python.c

Modified: freeswitch/trunk/src/mod/languages/mod_python/mod_python.c
==============================================================================
--- freeswitch/trunk/src/mod/languages/mod_python/mod_python.c	(original)
+++ freeswitch/trunk/src/mod/languages/mod_python/mod_python.c	Tue Jun  9 10:09:39 2009
@@ -59,7 +59,17 @@
 	char *xml_handler;
 } globals;
 
-static void eval_some_python(const char *funcname, char *args, switch_core_session_t *session, switch_stream_handle_t *stream, switch_event_t *params, char **str)
+struct switch_py_thread {
+	struct switch_py_thread *prev, *next; 
+	char *cmd;
+	char *args;
+	switch_memory_pool_t *pool;
+	PyThreadState *tstate;
+};
+struct switch_py_thread *thread_pool_head = NULL;
+static switch_mutex_t *THREAD_POOL_LOCK = NULL;
+
+static void eval_some_python(const char *funcname, char *args, switch_core_session_t *session, switch_stream_handle_t *stream, switch_event_t *params, char **str, struct switch_py_thread *pt)
 {
 	PyThreadState *tstate = NULL;
 	char *dupargs = NULL;
@@ -110,6 +120,11 @@
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error acquiring tstate\n");
 		goto done;
 	}
+	
+	/* Save state in thread struct so we can terminate it later if needed */
+	if (pt)
+		pt->tstate = tstate;
+
 	// swap in thread state
 	PyEval_AcquireThread(tstate);
 	init_freeswitch();
@@ -178,7 +193,8 @@
 		if (str) {
 			*str = strdup((char *) PyString_AsString(result));
 		}
-	} else {
+	} else if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
+		// Print error, but ignore SystemExit 
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error calling python script\n");
 		PyErr_Print();
 		PyErr_Clear();
@@ -224,7 +240,7 @@
 
 		switch_assert(mycmd);
 
-		eval_some_python("xml_fetch", mycmd, NULL, NULL, params, &str);
+		eval_some_python("xml_fetch", mycmd, NULL, NULL, params, &str, NULL);
 
 		if (str) {
 			if (switch_strlen_zero(str)) {
@@ -278,21 +294,36 @@
 
 SWITCH_STANDARD_APP(python_function)
 {
-	eval_some_python("handler", (char *) data, session, NULL, NULL, NULL);
+	eval_some_python("handler", (char *) data, session, NULL, NULL, NULL, NULL);
 
 }
 
-struct switch_py_thread {
-	char *args;
-	switch_memory_pool_t *pool;
-};
-
 static void *SWITCH_THREAD_FUNC py_thread_run(switch_thread_t *thread, void *obj)
 {
 	switch_memory_pool_t *pool;
 	struct switch_py_thread *pt = (struct switch_py_thread *) obj;
 
-	eval_some_python("runtime", pt->args, NULL, NULL, NULL, NULL);
+	/* Put thread in pool so we keep track of our threads */
+	switch_mutex_lock(THREAD_POOL_LOCK);
+	pt->next = thread_pool_head;
+	pt->prev = NULL;
+	if (pt->next)
+		pt->next->prev = pt;
+	thread_pool_head = pt;
+	switch_mutex_unlock(THREAD_POOL_LOCK);
+
+	/* Run the python script */
+	eval_some_python("runtime", pt->args, NULL, NULL, NULL, NULL, pt);
+
+	/* Thread is dead, remove from pool */
+	switch_mutex_lock(THREAD_POOL_LOCK);
+	if (pt->next)
+		pt->next->prev = pt->prev;
+	if (pt->prev)
+		pt->prev->next = pt->next;
+	if (thread_pool_head == pt)
+		thread_pool_head = pt->next;
+	switch_mutex_unlock(THREAD_POOL_LOCK);
 
 	pool = pt->pool;
 	switch_core_destroy_memory_pool(&pool);
@@ -303,7 +334,7 @@
 SWITCH_STANDARD_API(api_python)
 {
 	
-	eval_some_python("fsapi", (char *) cmd, session, stream, NULL, NULL);
+	eval_some_python("fsapi", (char *) cmd, session, stream, NULL, NULL, NULL);
 
 	return SWITCH_STATUS_SUCCESS;
 }
@@ -383,6 +414,8 @@
 		PyEval_ReleaseLock();
 	}
 
+	switch_mutex_init(&THREAD_POOL_LOCK, SWITCH_MUTEX_NESTED, pool);
+
 	do_config();
 
 	/* connect my internal structure to the blank pointer passed to me */
@@ -402,6 +435,53 @@
 {
 	PyInterpreterState *mainInterpreterState;
 	PyThreadState *myThreadState;
+	int thread_cnt = 0;
+	struct switch_py_thread *pt = NULL;
+	struct switch_py_thread *nextpt;
+	int i;
+
+	/* Kill all remaining threads */
+	pt = thread_pool_head;
+	PyEval_AcquireLock();
+	while (pt) {
+		thread_cnt ++;
+		nextpt = pt->next;
+		
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, 
+						  "Forcibly terminating script [%s]\n", pt->args);
+
+		/* Kill python script */
+		PyThreadState_Swap(pt->tstate);
+		PyThreadState_SetAsyncExc(pt->tstate->thread_id, PyExc_SystemExit);
+
+		pt = nextpt;
+	}
+	PyThreadState_Swap(mainThreadState);
+	PyEval_ReleaseLock();
+	switch_yield(1000000);
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, 
+					  "Had to kill %d threads\n", thread_cnt);
+
+	/* Give threads a few seconds to terminate. 
+	   Not using switch_thread_join() since if threads refuse to die
+	   then freeswitch will hang */
+	for (i=0; i < 10 && thread_pool_head; i++) {
+		switch_yield(1000000);
+	}
+	if (thread_pool_head) {
+		/* Not all threads died in time */
+		pt = thread_pool_head;
+		while (pt) {
+			nextpt = pt->next;
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, 
+						  "Script [%s] didn't exit in time\n", pt->args);
+			pt = nextpt;
+		}
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, 
+						  "Forcing python shutdown. This might cause freeswitch to crash!\n");
+	}
+
 
 	PyEval_AcquireLock();
 	mainInterpreterState = mainThreadState->interp;



More information about the Freeswitch-svn mailing list