dgaudet 99/06/20 14:46:14
Modified: mpm/src CHANGES mpm/src/include alloc.h mpm/src/main alloc.c mpm_prefork.c Log: remove 1.3 timeout code; SIGUSR1/SIGHUP/SIGTERM working again Revision Changes Path 1.7 +3 -0 apache-2.0/mpm/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/CHANGES,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- CHANGES 1999/06/20 21:12:47 1.6 +++ CHANGES 1999/06/20 21:46:11 1.7 @@ -1,5 +1,8 @@ Changes with MPM + * mpm_prefork: throw away all the alarm/timeout crud; and clean up the + signal handling for the new world order. [Dean Gaudet] + * Crude ap_thread_mutex abstraction so that we get the pthread stuff out of alloc.c for now. [Dean Gaudet] 1.2 +0 -22 apache-2.0/mpm/src/include/alloc.h Index: alloc.h =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/include/alloc.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- alloc.h 1999/06/18 18:39:27 1.1 +++ alloc.h 1999/06/20 21:46:12 1.2 @@ -287,10 +287,6 @@ * * Cleanups are identified for purposes of finding & running them off by the * plain_cleanup and data, which should presumably be unique. - * - * NB any code which invokes register_cleanup or kill_cleanup directly - * is a critical section which should be guarded by block_alarms() and - * unblock_alarms() below... */ API_EXPORT(void) ap_register_cleanup(pool *p, void *data, @@ -303,24 +299,6 @@ /* A "do-nothing" cleanup, for register_cleanup; it's faster to do * things this way than to test for NULL. */ API_EXPORT_NONSTD(void) ap_null_cleanup(void *data); - -/* The time between when a resource is actually allocated, and when it - * its cleanup is registered is a critical section, during which the - * resource could leak if we got interrupted or timed out. So, anything - * which registers cleanups should bracket resource allocation and the - * cleanup registry with these. (This is done internally by run_cleanup). - * - * NB they are actually implemented in http_main.c, since they are bound - * up with timeout handling in general... - */ - -#ifdef TPF -#define ap_block_alarms() (0) -#define ap_unblock_alarms() (0) -#else -API_EXPORT(void) ap_block_alarms(void); -API_EXPORT(void) ap_unblock_alarms(void); -#endif /* TPF */ /* Common cases which want utility support.. * the note_cleanups_for_foo routines are for 1.3 +4 -4 apache-2.0/mpm/src/main/alloc.c Index: alloc.c =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/main/alloc.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- alloc.c 1999/06/20 21:12:49 1.2 +++ alloc.c 1999/06/20 21:46:12 1.3 @@ -303,7 +303,7 @@ #define test_is_free(_x) #endif -/* Free a chain of blocks --- must be called with alarms blocked. */ +/* Free a chain of blocks --- must be called with mutex held. */ #ifdef POOL_DEBUG #define reset_block(b) do { \ test_is_free(b); \ @@ -383,7 +383,7 @@ /* Get a new block, from our own free list if possible, from the system - * if necessary. Must be called with alarms blocked. + * if necessary. Must be called with mutex held. */ static union block_hdr *new_block(int min_size) @@ -873,8 +873,8 @@ * until all the output is done. * * Note that this is completely safe because nothing else can - * allocate in this pool while ap_psprintf is running. alarms are - * blocked, and the only thing outside of alloc.c that's invoked + * allocate in this pool while ap_psprintf is running. + * The only thing outside of alloc.c that's invoked * is ap_vformatter -- which was purposefully written to be * self-contained with no callouts. */ 1.5 +21 -348 apache-2.0/mpm/src/main/mpm_prefork.c Index: mpm_prefork.c =================================================================== RCS file: /home/cvs/apache-2.0/mpm/src/main/mpm_prefork.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- mpm_prefork.c 1999/06/20 21:12:50 1.4 +++ mpm_prefork.c 1999/06/20 21:46:13 1.5 @@ -82,7 +82,6 @@ * TODO: behave like apache-1.3... here's a short list of things I think * TODO: need cleaning up still: * TODO: - use ralf's mm stuff for the shared mem and mutexes - * TODO: - eliminate the timeout stuff... 2.0 timeouts are part of the BUFF * TODO: - abstract the Listen stuff, it's going to be common with other MPM * TODO: - clean up scoreboard stuff when we figure out how to do it in 2.0 */ @@ -161,7 +160,6 @@ /* *Non*-shared http_main globals... */ static server_rec *server_conf; -static JMP_BUF jmpbuffer; static int sd; static fd_set listenfds; static int listenmaxfd; @@ -180,11 +178,6 @@ static int one_process = 0; -/* set if timeouts are to be handled by the children and not by the parent. - * i.e. child_timeouts = !standalone || one_process. - */ -static int child_timeouts; - #ifdef HAS_OTHER_CHILD /* used to maintain list of children which aren't part of the scoreboard */ typedef struct other_child_rec other_child_rec; @@ -804,220 +797,7 @@ #define SAFE_ACCEPT(stmt) do {stmt;} while(0) #endif -/***************************************************************** - * - * Timeout handling. DISTINCTLY not thread-safe, but all this stuff - * has to change for threads anyway. Note that this code allows only - * one timeout in progress at a time... - */ - -static conn_rec *volatile current_conn; -static request_rec *volatile timeout_req; -static const char *volatile timeout_name = NULL; -static int volatile alarms_blocked = 0; -static int volatile alarm_pending = 0; - -static void timeout(int sig) -{ - void *dirconf; - - if (alarms_blocked) { - alarm_pending = 1; - return; - } - if (exit_after_unblock) { - clean_child_exit(0); - } - - if (!current_conn) { - ap_longjmp(jmpbuffer, 1); - } - - if (timeout_req != NULL) - dirconf = timeout_req->per_dir_config; - else - dirconf = current_conn->base_server->lookup_defaults; - if (!current_conn->keptalive) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, - current_conn->base_server, "[client %s] %s timed out", - current_conn->remote_ip, - timeout_name ? timeout_name : "request"); - } - - if (timeout_req) { - /* Someone has asked for this transaction to just be aborted - * if it times out... - */ - - request_rec *log_req = timeout_req; - request_rec *save_req = timeout_req; - - /* avoid looping... if ap_log_transaction started another - * timer (say via rfc1413.c) we could loop... - */ - timeout_req = NULL; - - while (log_req->main || log_req->prev) { - /* Get back to original request... */ - if (log_req->main) - log_req = log_req->main; - else - log_req = log_req->prev; - } - - if (!current_conn->keptalive) { - /* in some cases we come here before setting the time */ - if (log_req->request_time == 0) { - log_req->request_time = time(0); - } - ap_log_transaction(log_req); - } - - ap_bsetflag(save_req->connection->client, B_EOUT, 1); - ap_bclose(save_req->connection->client); - - ap_longjmp(jmpbuffer, 1); - } - else { /* abort the connection */ - ap_bsetflag(current_conn->client, B_EOUT, 1); - ap_bclose(current_conn->client); - current_conn->aborted = 1; - } -} - -#ifndef TPF -/* - * These two called from alloc.c to protect its critical sections... - * Note that they can nest (as when destroying the sub_pools of a pool - * which is itself being cleared); we have to support that here. - */ - -API_EXPORT(void) ap_block_alarms(void) -{ - ++alarms_blocked; -} - -API_EXPORT(void) ap_unblock_alarms(void) -{ - --alarms_blocked; - if (alarms_blocked == 0) { - if (exit_after_unblock) { - /* We have a couple race conditions to deal with here, we can't - * allow a timeout that comes in this small interval to allow - * the child to jump back to the main loop. Instead we block - * alarms again, and then note that exit_after_unblock is - * being dealt with. We choose this way to solve this so that - * the common path through unblock_alarms() is really short. - */ - ++alarms_blocked; - exit_after_unblock = 0; - clean_child_exit(0); - } - if (alarm_pending) { - alarm_pending = 0; - timeout(0); - } - } -} -#endif /* TPF */ - -static void (*volatile alarm_fn) (int) = NULL; - -static void alrm_handler(int sig) -{ - if (alarm_fn) { - (*alarm_fn) (sig); - } -} - -unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x) -{ - unsigned int old; - - if (alarm_fn && x && fn != alarm_fn) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, - "ap_set_callback_and_alarm: possible nested timer!"); - } - alarm_fn = fn; -#ifndef OPTIMIZE_TIMEOUTS - old = alarm(x); -#else - if (child_timeouts) { - old = alarm(x); - } - else { - /* Just note the timeout in our scoreboard, no need to call the system. - * We also note that the virtual time has gone forward. - */ - ap_check_signals(); - old = ap_scoreboard_image->servers[my_child_num].timeout_len; - ap_scoreboard_image->servers[my_child_num].timeout_len = x; - ++ap_scoreboard_image->servers[my_child_num].cur_vtime; - } -#endif - return (old); -} - - -/* reset_timeout (request_rec *) resets the timeout in effect, - * as long as it hasn't expired already. - */ - -API_EXPORT(void) ap_reset_timeout(request_rec *r) -{ - int i; - - if (timeout_name) { /* timeout has been set */ - i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout); - if (i == 0) /* timeout already expired, so set it back to 0 */ - ap_set_callback_and_alarm(alarm_fn, 0); - } -} - - - - -void ap_keepalive_timeout(char *name, request_rec *r) -{ - unsigned int to; - timeout_req = r; - timeout_name = name; - - if (r->connection->keptalive) - to = r->server->keep_alive_timeout; - else - to = r->server->timeout; - ap_set_callback_and_alarm(timeout, to); - -} - -API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r) -{ - timeout_req = r; - timeout_name = name; - - ap_set_callback_and_alarm(timeout, r->server->timeout); - -} - -API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r) -{ - timeout_name = name; - - ap_set_callback_and_alarm(timeout, r->server->timeout); - -} - -API_EXPORT(void) ap_kill_timeout(request_rec *dummy) -{ - ap_check_signals(); - ap_set_callback_and_alarm(NULL, 0); - timeout_req = NULL; - timeout_name = NULL; -} - - /***************************************************************** * dealing with other children */ @@ -1635,14 +1415,8 @@ ss = &ap_scoreboard_image->servers[child_num]; old_status = ss->status; ss->status = status; -#ifdef OPTIMIZE_TIMEOUTS - ++ss->cur_vtime; -#endif if (ap_extended_status) { -#ifndef OPTIMIZE_TIMEOUTS - ss->last_used = time(NULL); -#endif if (status == SERVER_READY || status == SERVER_DEAD) { /* * Reset individual counters @@ -2107,19 +1881,12 @@ */ static void just_die(int sig) -{ /* SIGHUP to child process??? */ - /* if alarms are blocked we have to wait to die otherwise we might - * end up with corruption in alloc.c's internal structures */ - if (alarms_blocked) { - exit_after_unblock = 1; - } - else { - clean_child_exit(0); - } +{ + clean_child_exit(0); } -static int volatile usr1_just_die = 1; static int volatile deferred_die; +static int volatile usr1_just_die; static void usr1_handler(int sig) { @@ -2134,20 +1901,8 @@ static int volatile restart_pending; static int volatile is_graceful; ap_generation_t volatile ap_my_generation=0; - -/* - * ap_start_shutdown() and ap_start_restart(), below, are a first stab at - * functions to initiate shutdown or restart without relying on signals. - * Previously this was initiated in sig_term() and restart() signal handlers, - * but we want to be able to start a shutdown/restart from other sources -- - * e.g. on Win32, from the service manager. Now the service manager can - * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that - * these functions can also be called by the child processes, since global - * variables are no longer used to pass on the required action to the parent. - */ - -void ap_start_shutdown(void) +static void sig_term(int sig) { if (shutdown_pending == 1) { /* Um, is this _probably_ not an error, if the user has @@ -2159,25 +1914,14 @@ shutdown_pending = 1; } -/* do a graceful restart if graceful == 1 */ -void ap_start_restart(int graceful) +static void restart(int sig) { if (restart_pending == 1) { /* Probably not an error - don't bother reporting it */ return; } restart_pending = 1; - is_graceful = graceful; -} - -static void sig_term(int sig) -{ - ap_start_shutdown(); -} - -static void restart(int sig) -{ - ap_start_restart(sig == SIGUSR1); + is_graceful = sig == SIGUSR1; } static void set_signals(void) @@ -2484,11 +2228,9 @@ ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); /* note that because we're about to slack we don't use psocket */ - ap_block_alarms(); if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: failed to get a socket for %s", addr); - ap_unblock_alarms(); exit(1); } @@ -2527,7 +2269,6 @@ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); close(s); - ap_unblock_alarms(); return -1; } #endif /*_OSD_POSIX*/ @@ -2537,7 +2278,6 @@ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); close(s); - ap_unblock_alarms(); return -1; } #endif @@ -2589,7 +2329,6 @@ GETUSERMODE(); #endif close(s); - ap_unblock_alarms(); exit(1); } #ifdef MPE @@ -2601,7 +2340,6 @@ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "make_sock: unable to listen for connections on %s", addr); close(s); - ap_unblock_alarms(); exit(1); } @@ -2610,7 +2348,6 @@ ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ #endif - ap_unblock_alarms(); #ifdef CHECK_FD_SETSIZE /* protect various fd_sets */ @@ -2781,7 +2518,8 @@ int ap_mpm_graceful_stop(void) { ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + if (deferred_die || + ap_scoreboard_image->global.running_generation != ap_my_generation) { return 1; } return 0; @@ -2794,18 +2532,7 @@ struct sockaddr sa_client; listen_rec *lr; pool *ptrans; - - /* All of initialization is a critical section, we don't care if we're - * told to HUP or USR1 before we're done initializing. For example, - * we could be half way through ap_child_init_hook() when a restart - * signal arrives, and we'd have no real way to recover gracefully - * and exit properly. - * - * I suppose a module could take forever to initialize, but that would - * be either a broken module, or a broken configuration (i.e. network - * problems, file locking problems, whatever). -djg - */ - ap_block_alarms(); + conn_rec *current_conn; my_pid = getpid(); csd = -1; @@ -2851,26 +2578,10 @@ ap_child_init_hook(pchild, server_conf); - /* done with the initialization critical section */ - ap_unblock_alarms(); - (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); - /* - * Setup the jump buffers so that we can return here after a timeout - */ - ap_setjmp(jmpbuffer); -#ifndef OS2 -#ifdef SIGURG - signal(SIGURG, timeout); -#endif -#endif - signal(SIGALRM, alrm_handler); -#ifdef TPF signal(SIGHUP, just_die); signal(SIGTERM, just_die); - signal(SIGUSR1, just_die); -#endif /* TPF */ #ifdef OS2 /* Stop Ctrl-C/Ctrl-Break signals going to child processes */ @@ -2884,9 +2595,8 @@ BUFF *conn_io; /* Prepare to receive a SIGUSR1 due to graceful restart so that - * we can exit cleanly. Since we're between connections right - * now it's the right time to exit, but we might be blocked in a - * system call when the graceful restart request is made. */ + * we can exit cleanly. + */ usr1_just_die = 1; signal(SIGUSR1, usr1_handler); @@ -2894,16 +2604,10 @@ * (Re)initialize this child to a pre-connection state. */ - ap_kill_timeout(0); /* Cancel any outstanding alarms. */ current_conn = NULL; ap_clear_pool(ptrans); - ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.running_generation != ap_my_generation) { - clean_child_exit(0); - } - if ((ap_max_requests_per_child > 0 && requests_this_child++ >= ap_max_requests_per_child)) { clean_child_exit(0); @@ -2951,17 +2655,16 @@ /* if we accept() something we don't want to die, so we have to * defer the exit */ - deferred_die = 0; usr1_just_die = 0; for (;;) { - clen = sizeof(sa_client); - csd = ap_accept(sd, &sa_client, &clen); - if (csd >= 0 || errno != EINTR) - break; if (deferred_die) { /* we didn't get a socket, and we were told to die */ clean_child_exit(0); } + clen = sizeof(sa_client); + csd = ap_accept(sd, &sa_client, &clen); + if (csd >= 0 || errno != EINTR) + break; } if (csd >= 0) @@ -3038,19 +2741,10 @@ } } - /* go around again, safe to die */ - usr1_just_die = 1; - if (deferred_die) { - /* ok maybe not, see ya later */ + if (ap_mpm_graceful_stop()) { clean_child_exit(0); } - /* or maybe we missed a signal, you never know on systems - * without reliable signals - */ - ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.running_generation != ap_my_generation) { - clean_child_exit(0); - } + usr1_just_die = 1; } SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ @@ -3061,7 +2755,9 @@ #endif /* We've got a socket, let's at least process one request off the - * socket before we accept a graceful restart request. + * socket before we accept a graceful restart request. We set + * the signal to ignore because we don't want to disturb any + * third party code. */ signal(SIGUSR1, SIG_IGN); @@ -3218,9 +2914,6 @@ child_main(slot); } -#ifdef OPTIMIZE_TIMEOUTS - ap_scoreboard_image->parent[slot].last_rtime = now; -#endif ap_scoreboard_image->parent[slot].pid = pid; #ifdef SCOREBOARD_FILE lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0); @@ -3317,24 +3010,6 @@ ++total_non_dead; last_non_dead = i; -#ifdef OPTIMIZE_TIMEOUTS - if (ss->timeout_len) { - /* if it's a live server, with a live timeout then - * start checking its timeout */ - parent_score *ps = &ap_scoreboard_image->parent[i]; - if (ss->cur_vtime != ps->last_vtime) { - /* it has made progress, so update its last_rtime, - * last_vtime */ - ps->last_rtime = now; - ps->last_vtime = ss->cur_vtime; - } - else if (ps->last_rtime + ss->timeout_len < now) { - /* no progress, and the timeout length has been exceeded */ - ss->timeout_len = 0; - kill(ps->pid, SIGALRM); - } - } -#endif } } max_daemons_limit = last_non_dead + 1; @@ -3463,8 +3138,6 @@ server_conf = s; - child_timeouts = one_process; - ap_log_pid(pconf, ap_pid_fname); setup_listeners(pconf); @@ -3687,7 +3360,7 @@ is_graceful = 0; if (!one_process) { - /* TODO: detach(); ... it should work fine, this is just easier for debugging */ + detach(); } my_pid = getpid();