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


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@171120 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Tilghman Lesher
2009-01-25 20:30:41 +00:00
parent 52b97107ea
commit 2957e5ae8e

View 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);