rbb 99/02/11 14:15:56
Modified: pthreads/src/main fdqueue.c http_main.c Log: Another pass at logic for signal handling. SIGTERM works properly. SIGHUP causes a zombie, and makes the socket unusable. Graceful restarts are not close to working, because of signals issues with user based pthreads. They are on the plate for tomorrow. Manoj and I worked on this together, both design and implementation. Revision Changes Path 1.3 +2 -3 apache-apr/pthreads/src/main/fdqueue.c Index: fdqueue.c =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/fdqueue.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- fdqueue.c 1999/02/09 22:04:14 1.2 +++ fdqueue.c 1999/02/11 22:15:55 1.3 @@ -49,9 +49,8 @@ if (pthread_mutex_lock(&queue->one_big_mutex) != 0) { return FD_QUEUE_FAILURE; } - while (queue->head == queue->tail) { - pthread_cond_wait(&queue->not_empty, &queue->one_big_mutex); - } + pthread_cond_wait(&queue->not_empty, &queue->one_big_mutex); + fd = queue->data[queue->head].fd; *addr = queue->data[queue->head].addr; /* If the queue was full, signal that it no longer is */ 1.17 +100 -61 apache-apr/pthreads/src/main/http_main.c Index: http_main.c =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/http_main.c,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- http_main.c 1999/02/11 16:33:04 1.16 +++ http_main.c 1999/02/11 22:15:55 1.17 @@ -175,7 +175,9 @@ int ap_listenbacklog; int ap_dump_settings = 0; API_VAR_EXPORT int ap_extended_status = 0; +pthread_once_t firstcall = PTHREAD_ONCE_INIT; + /* * The max child slot ever assigned, preserved across restarts. Necessary * to deal with MaxClients changes across SIGUSR1 restarts. We use this @@ -480,15 +482,58 @@ } } +void graceful_killer(void) +{ + listen_rec *lr; + int i; + + for (i = 0; i < ap_threads_per_child; i++) { + pthread_cond_signal(&(csd_queue.not_empty)); + } + /* Setup acceptor threads */ + + lr = ap_listeners; + while (lr->next != NULL) { + close(lr->fd); + lr= lr->next; + } + fprintf(stderr, "gonna exit doit"); + pthread_exit(NULL); +} + +static int find_child_by_pid(pid_t pid) /* ZZZ */ +{ + int i; + + for (i = 0; i < max_daemons_limit; ++i) + if (ap_scoreboard_image->parent[i].pid == pid) + return i; + + return -1; +} + /* a clean exit from a child with proper cleanup static void clean_child_exit(int code) __attribute__ ((noreturn)); */ static void clean_child_exit(int code) { + int child_num = find_child_by_pid(getpid()); + int i; + listen_rec *lr; + + /* lr = ap_listeners; + while (lr->next != NULL) { + close(lr->fd); + lr= lr->next; + }*/ + + for (i = 0; i < max_threads_limit; i++) + ap_update_child_status(child_num, i, SERVER_DEAD, (request_rec *) NULL); + if (pchild) { ap_child_exit_modules(pchild, server_conf); ap_destroy_pool(pchild); } - /* longjump(tls()->thread_exit, 1); */ + exit(code); } @@ -834,8 +879,6 @@ if (child_num < 0) return -1; - fprintf(stderr, "Updating status for %d %d to %d\n", child_num, thread_num, status); - ss = &ap_scoreboard_image->servers[child_num][thread_num]; old_status = ss->status; ss->status = status; @@ -932,17 +975,6 @@ } -static int find_child_by_pid(pid_t pid) /* ZZZ */ -{ - int i; - - for (i = 0; i < max_daemons_limit; ++i) - if (ap_scoreboard_image->parent[i].pid == pid) - return i; - - return -1; -} - static void reclaim_child_processes(int terminate) { int i, status; @@ -985,18 +1017,9 @@ case 2: /* 82ms */ break; case 3: /* 344ms */ - /* perhaps it missed the SIGHUP, lets try again */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, - server_conf, - "child process %d did not exit, sending another SIGHUP", - pid); - kill(pid, SIGHUP); - waittime = 1024 * 16; - break; case 4: /* 16ms */ case 5: /* 82ms */ case 6: /* 344ms */ - break; case 7: /* 1.4sec */ /* ok, now it's being annoying */ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, @@ -1266,15 +1289,12 @@ static int volatile usr1_just_die = 1; static int volatile deferred_die; -static void usr1_handler(int sig) +static void graceful_sig_handler(int sig) { - if (usr1_just_die) { - just_die(sig); - } - deferred_die = 1; + fprintf(stderr, "Got a HUP from parent"); + requests_this_child = 0; } - /***************************************************************** * Connection structures and accounting... */ @@ -1319,6 +1339,8 @@ /* do a graceful restart if graceful == 1 */ void ap_start_restart(int graceful) { + + fprintf(stderr, " SENDING A RESTART MESSAGE!!"); if (restart_pending == 1) { /* Probably not an error - don't bother reporting it */ return; @@ -1398,14 +1420,14 @@ sigaddset(&sa.sa_mask, SIGHUP); #ifndef LINUX sigaddset(&sa.sa_mask, SIGUSR1); -#endif /* LINUX */ +#endif sa.sa_handler = restart; if (sigaction(SIGHUP, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); -#ifndef LINUX +#ifndef LINUX if (sigaction(SIGUSR1, &sa, NULL) < 0) ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)"); -#endif /* LINUX */ +#endif #else if (!one_process) { signal(SIGSEGV, sig_coredump); @@ -1431,14 +1453,14 @@ signal(SIGTERM, sig_term); #ifdef SIGHUP - signal(SIGHUP, restart); + signal(SIGHUP, sig_hup); #endif /* SIGHUP */ #ifndef LINUX #ifdef SIGUSR1 signal(SIGUSR1, restart); #endif /* SIGUSR1 */ -#endif /* LINUX */ #endif +#endif } /***************************************************************** @@ -1572,7 +1594,7 @@ "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); printf("make_sock: failed to setsockopt for %s\n", addr); close(s); - return NULL; + return 0; } #endif /*_OSD_POSIX*/ one = 1; @@ -1582,7 +1604,7 @@ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); close(s); - return NULL; + return 0; } #endif #endif @@ -1663,7 +1685,7 @@ "found, you probably need to rebuild Apache with a " "larger FD_SETSIZE", addr, s, FD_SETSIZE); close(s); - return NULL; + return 0; } #endif @@ -1718,7 +1740,7 @@ return or->fd; } } - return NULL; + return 0; } @@ -1747,7 +1769,7 @@ num_listenfds = 0; for (;;) { fd = find_listener(lr); - if (fd == NULL) { + if (fd == 0) { fd = make_sock(p, &lr->local_addr); } else { @@ -2038,23 +2060,27 @@ * Let's setup the socket options on the master socket. These * will be inherited by any dup'ed sockets for us. No reason to do * this oce for each request. - */ - sock_disable_nagle(sd); + sock_disable_nagle(sd); + */ (void) ap_update_child_status(my_pid, my_tid, SERVER_READY, (request_rec *) NULL); - for (;;) { + while (0 < requests_this_child) { csd = accept(sd, &sa_client, &len); if (csd >= 0) { if (queue_push(&csd_queue, csd, &sa_client) != 0) { /* ap_log_error*/ - } + } } else{ - /* ap_log_error()*/ - } - /* thread_exit */ + /* ap_log_error()*/ + } } + ap_update_child_status(my_pid, my_tid, SERVER_DEAD, (request_rec *) NULL); + pthread_once(&firstcall, graceful_killer); + fprintf(stderr, "gonna exit accept thread"); + + pthread_exit(NULL); } void * worker_thread(void * dummy) @@ -2068,12 +2094,18 @@ (void) ap_update_child_status(my_pid, my_tid, SERVER_READY, (request_rec *) NULL); - - for (;;) { + + while (0 < requests_this_child) { int csd = queue_pop(&csd_queue, &sa_client); - pthread_t tid = pthread_self(); - process_socket(pchild, &sa_client, csd, my_pid, my_tid); + if (csd >= 0) { + process_socket(pchild, &sa_client, csd, my_pid, my_tid); + } } + ap_update_child_status(my_pid, my_tid, SERVER_DEAD, (request_rec *) NULL); + pthread_once(&firstcall, graceful_killer); + fprintf(stderr, "gonna exit worker thread"); + + pthread_exit(NULL); } /***************************************************************** @@ -2211,10 +2243,12 @@ pthread_attr_t thread_attr; my_pid = getpid(); - requests_this_child = 0; + requests_this_child = ap_max_requests_per_child; pchild = ap_make_sub_pool(pconf); + pthread_detach(pthread_self()); + /*stuff to do before we switch id's, so we have permissions.*/ reopen_scoreboard(pchild); /* SAFE_ACCEPT(accept_mutex_child_init(pchild));*/ @@ -2237,6 +2271,7 @@ /* We don't want to have to pthread_wait on these threads */ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); +#if 0 /* Set signal masks for threads to basically nothing */ sigemptyset(&sig_mask); #ifdef LINUX @@ -2246,6 +2281,8 @@ if ((ret = pthread_sigmask(SIG_SETMASK, &sig_mask, NULL)) != 0) { ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, "pthread_sigmask failed"); } +#endif + /* Setup worker threads */ for (i=0; i < ap_threads_per_child; i++) { my_info = NULL; @@ -2379,7 +2416,7 @@ RAISE_SIGSTOP(MAKE_CHILD); MONCONTROL(1); - signal(SIGHUP, just_die); + signal(SIGHUP, graceful_sig_handler); signal(SIGTERM, just_die); child_main(slot); @@ -2445,7 +2482,7 @@ if (i >= max_daemons_limit && free_length == idle_spawn_rate) break; - for (j = 0; j < max_threads_limit; j++) { + for (j = 0; j < ap_threads_per_child; j++) { ss = &ap_scoreboard_image->servers[i][j]; status = ss->status; if (status == SERVER_DEAD) { @@ -2488,7 +2525,7 @@ * shut down gracefully, in case it happened to pick up a request * while we were counting */ - /* kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1); */ + kill(ap_scoreboard_image->parent[to_kill].pid, SIGHUP); idle_spawn_rate = 1; } else if (idle_count < ap_daemons_min_free) { @@ -2724,11 +2761,10 @@ "SIGUSR1 received. Doing graceful restart"); /* kill off the idle ones */ -#if 0 - if (ap_killpg(pgrp, SIGUSR1) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1"); + if (ap_killpg(pgrp, SIGHUP) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP"); } -#endif + /* This is mostly for debugging... so that we know what is still * gracefully dealing with existing request. */ @@ -2742,9 +2778,12 @@ } } else { - /* Kill 'em all */ - if (ap_killpg(pgrp, SIGHUP) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP"); + /* Kill 'em all. Since the child acts the same on the parents SIGTERM + * and a SIGHUP, we may as well use the same signal, because some user + * pthreads are stealing signals from us left and right. + */ + if (ap_killpg(pgrp, SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); } ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, "SIGHUP received. Attempting to restart");