]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Add thread to kill zombies, when child processes don't die immediately on
authorTilghman Lesher <tilghman@meg.abyt.es>
Sun, 25 Jan 2009 20:30:41 +0000 (20:30 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Sun, 25 Jan 2009 20:30:41 +0000 (20:30 +0000)
SIGHUP.
(closes issue #13968)
 Reported by: eldadran
 Patches:
       20090114__bug13968.diff.txt uploaded by Corydon76 (license 14)
 Tested by: eldadran

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@171120 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_agi.c

index a80198bf304e6ab7c0dfdabdb050778c4c4a971d..91f15b86ed1bf7a318797279dd4e4a66a4f3fce0 100644 (file)
@@ -105,6 +105,8 @@ static char *descrip =
 
 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 */
@@ -119,6 +121,13 @@ enum agi_result {
        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, ...)
 {
        char *stuff;
@@ -1969,7 +1978,26 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                                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 @@ static struct ast_cli_entry cli_agi[] = {
        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);