[asterisk-commits] tilghman: branch 1.4 r171120 - /branches/1.4/res/res_agi.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Jan 25 14:30:42 CST 2009


Author: tilghman
Date: Sun Jan 25 14:30:41 2009
New Revision: 171120

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=171120
Log:
Add thread to kill zombies, when child processes don't die immediately on
SIGHUP.
(closes issue #13968)
 Reported by: eldadran
 Patches: 
       20090114__bug13968.diff.txt uploaded by Corydon76 (license 14)
 Tested by: eldadran

Modified:
    branches/1.4/res/res_agi.c

Modified: branches/1.4/res/res_agi.c
URL: http://svn.digium.com/svn-view/asterisk/branches/1.4/res/res_agi.c?view=diff&rev=171120&r1=171119&r2=171120
==============================================================================
--- branches/1.4/res/res_agi.c (original)
+++ branches/1.4/res/res_agi.c Sun Jan 25 14:30:41 2009
@@ -105,6 +105,8 @@
 
 static int agidebug = 0;
 
+static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+
 #define TONE_BLOCK_SIZE 200
 
 /* Max time to connect to an AGI remote host */
@@ -118,6 +120,13 @@
 	AGI_RESULT_SUCCESS_FAST,
 	AGI_RESULT_HANGUP
 };
+
+struct zombie {
+	pid_t pid;
+	AST_LIST_ENTRY(zombie) list;
+};
+
+static AST_LIST_HEAD_STATIC(zombies, zombie);
 
 static int __attribute__((format(printf, 2, 3))) agi_debug_cli(int fd, char *fmt, ...)
 {
@@ -1969,7 +1978,26 @@
 				usleep(1);
 			}
 		}
-		waitpid(pid, status, WNOHANG);
+		/* This is essentially doing the same as without WNOHANG, except that
+		 * it allows the main thread to proceed, even without the child PID
+		 * dying immediately (the child may be doing cleanup, etc.).  Without
+		 * this code, zombie processes accumulate for as long as child
+		 * processes exist (which on busy systems may be never, filling up the
+		 * process table).
+		 *
+		 * Note that in trunk, we don't stop interaction at the hangup event
+		 * (instead we transparently switch to DeadAGI operation), so this is a
+		 * short-lived code addition.
+		 */
+		if (waitpid(pid, status, WNOHANG) == 0) {
+			struct zombie *cur = ast_calloc(1, sizeof(*cur));
+			if (cur) {
+				cur->pid = pid;
+				AST_LIST_LOCK(&zombies);
+				AST_LIST_INSERT_TAIL(&zombies, cur, list);
+				AST_LIST_UNLOCK(&zombies);
+			}
+		}
 	}
 	fclose(readf);
 	return returnstatus;
@@ -2203,17 +2231,58 @@
 	dumpagihtml_help, NULL, &cli_dump_agihtml_deprecated },
 };
 
+static void *shaun_of_the_dead(void *data)
+{
+	struct zombie *cur;
+	int status;
+	for (;;) {
+		if (!AST_LIST_EMPTY(&zombies)) {
+			/* Don't allow cancellation while we have a lock. */
+			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+			AST_LIST_LOCK(&zombies);
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
+				if (waitpid(cur->pid, &status, WNOHANG) != 0) {
+					AST_LIST_REMOVE_CURRENT(&zombies, list);
+					ast_free(cur);
+				}
+			}
+			AST_LIST_TRAVERSE_SAFE_END
+			AST_LIST_UNLOCK(&zombies);
+			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+		}
+		pthread_testcancel();
+		/* Wait for 60 seconds, without engaging in a busy loop. */
+		poll(NULL, 0, 60000);
+	}
+	return NULL;
+}
+
 static int unload_module(void)
 {
+	int res;
+	struct zombie *cur;
 	ast_module_user_hangup_all();
 	ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
 	ast_unregister_application(eapp);
 	ast_unregister_application(deadapp);
-	return ast_unregister_application(app);
+	res = ast_unregister_application(app);
+	if (shaun_of_the_dead_thread != AST_PTHREADT_NULL) {
+		pthread_cancel(shaun_of_the_dead_thread);
+		pthread_kill(shaun_of_the_dead_thread, SIGURG);
+		pthread_join(shaun_of_the_dead_thread, NULL);
+	}
+	while ((cur = AST_LIST_REMOVE_HEAD(&zombies, list))) {
+		ast_free(cur);
+	}
+	return res;
 }
 
 static int load_module(void)
 {
+	if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
+		ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
+		shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+	}
 	ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
 	ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
 	ast_register_application(eapp, eagi_exec, esynopsis, descrip);




More information about the asterisk-commits mailing list