dgaudet 98/12/04 11:12:18
Modified: src/include ap_mmn.h scoreboard.h src/main http_main.c src/modules/standard mod_status.c Log: - Jim's fix to the vhostrec problem wasn't sufficient for all cases... implement the full generation-based solution I proposed. - Deal with generation rollover a little more robustly. Still not perfect. Revision Changes Path 1.12 +5 -2 apache-1.3/src/include/ap_mmn.h Index: ap_mmn.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/ap_mmn.h,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- ap_mmn.h 1998/11/20 21:17:25 1.11 +++ ap_mmn.h 1998/12/04 19:12:15 1.12 @@ -184,11 +184,14 @@ * 19981108 (1.3.4-dev) - added ap_method_number_of() * - changed value of M_INVALID and added WebDAV methods * 19981108.1 - ap_exists_config_define() is now public (minor bump) - * + * 19981204 - scoreboard changes -- added generation, changed + * exit_generation to running_generation. Somewhere + * earlier vhostrec was added, but it's only safe to use + * as of this rev. See scoreboard.h for documentation. */ #ifndef MODULE_MAGIC_NUMBER_MAJOR -#define MODULE_MAGIC_NUMBER_MAJOR 19981108 +#define MODULE_MAGIC_NUMBER_MAJOR 19981204 #endif #define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */ #define MODULE_MAGIC_NUMBER MODULE_MAGIC_NUMBER_MAJOR /* backward compat */ 1.44 +29 -2 apache-1.3/src/include/scoreboard.h Index: scoreboard.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/scoreboard.h,v retrieving revision 1.43 retrieving revision 1.44 diff -u -r1.43 -r1.44 --- scoreboard.h 1998/11/06 22:42:41 1.43 +++ scoreboard.h 1998/12/04 19:12:15 1.44 @@ -106,6 +106,29 @@ */ typedef unsigned vtime_t; +/* Type used for generation indicies. Startup and every restart cause a + * new generation of children to be spawned. Children within the same + * generation share the same configuration information -- pointers to stuff + * created at config time in the parent are valid across children. For + * example, the vhostrec pointer in the scoreboard below is valid in all + * children of the same generation. + * + * The safe way to access the vhost pointer is like this: + * + * short_score *ss = pointer to whichver slot is interesting; + * parent_score *ps = pointer to whichver slot is interesting; + * server_rec *vh = ss->vhostrec; + * + * if (ps->generation != ap_my_generation) { + * vh = NULL; + * } + * + * then if vh is not NULL it's valid in this child. + * + * This avoids various race conditions around restarts. + */ +typedef int ap_generation_t; + /* stuff which the children generally write, and the parent mainly reads */ typedef struct { #ifdef OPTIMIZE_TIMEOUTS @@ -135,11 +158,12 @@ char client[32]; /* Keep 'em small... */ char request[64]; /* We just want an idea... */ server_rec *vhostrec; /* What virtual host is being accessed? */ + /* SEE ABOVE FOR SAFE USAGE! */ } short_score; typedef struct { - int exit_generation; /* Set by the main process if a graceful - restart is required */ + ap_generation_t running_generation; /* the generation of children which + * should still be serving requests. */ } global_score; /* stuff which the parent generally writes and the children rarely read */ @@ -149,6 +173,7 @@ time_t last_rtime; /* time(0) of the last change */ vtime_t last_vtime; /* the last vtime the parent has seen */ #endif + ap_generation_t generation; /* generation of this child */ } parent_score; typedef struct { @@ -163,6 +188,8 @@ API_EXPORT(int) ap_exists_scoreboard_image(void); API_VAR_EXPORT extern scoreboard *ap_scoreboard_image; + +API_VAR_EXPORT extern ap_generation_t ap_my_generation; /* for time_process_request() in http_main.c */ #define START_PREQUEST 1 1.408 +56 -60 apache-1.3/src/main/http_main.c Index: http_main.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v retrieving revision 1.407 retrieving revision 1.408 diff -u -r1.407 -r1.408 --- http_main.c 1998/12/03 01:07:54 1.407 +++ http_main.c 1998/12/04 19:12:16 1.408 @@ -1531,12 +1531,6 @@ * We begin with routines which deal with the file itself... */ -/* volatile just in case */ -static int volatile shutdown_pending; -static int volatile restart_pending; -static int volatile is_graceful; -static int volatile generation; - #ifdef MULTITHREAD /* * In the multithreaded mode, have multiple threads - not multiple @@ -1548,14 +1542,8 @@ static void reinit_scoreboard(pool *p) { ap_assert(!ap_scoreboard_image); - if (is_graceful) { - int i; - for (i = 0; i < HARD_SERVER_LIMIT; i++) - ap_scoreboard_image->servers[i].vhostrec = NULL; - } else { - ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); - memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); - } + ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); } void cleanup_scoreboard(void) @@ -1636,7 +1624,7 @@ ap_server_argv0); } ap_scoreboard_image = (scoreboard *) m; - ap_scoreboard_image->global.exit_generation = 0; + ap_scoreboard_image->global.running_generation = 0; } static void reopen_scoreboard(pool *p) @@ -1723,7 +1711,7 @@ close(fd); ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup); ap_scoreboard_image = (scoreboard *) m; - ap_scoreboard_image->global.exit_generation = 0; + ap_scoreboard_image->global.running_generation = 0; } static void reopen_scoreboard(pool *p) @@ -1803,7 +1791,7 @@ close(fd); #endif ap_scoreboard_image = (scoreboard *) m; - ap_scoreboard_image->global.exit_generation = 0; + ap_scoreboard_image->global.running_generation = 0; } static void reopen_scoreboard(pool *p) @@ -1895,7 +1883,7 @@ "sbrk() could not move break back"); } #endif - ap_scoreboard_image->global.exit_generation = 0; + ap_scoreboard_image->global.running_generation = 0; } static void reopen_scoreboard(pool *p) @@ -1962,41 +1950,32 @@ /* Called by parent process */ static void reinit_scoreboard(pool *p) { - if (is_graceful && ap_scoreboard_image) { - int i; - for (i = 0; i < HARD_SERVER_LIMIT; i++) - ap_scoreboard_image->servers[i].vhostrec = NULL; -#ifdef SCOREBOARD_FILE - force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); -#endif - } else { - int exit_gen = 0; - if (ap_scoreboard_image) - exit_gen = ap_scoreboard_image->global.exit_generation; + int running_gen = 0; + if (ap_scoreboard_image) + running_gen = ap_scoreboard_image->global.running_generation; #ifndef SCOREBOARD_FILE - if (ap_scoreboard_image == NULL) { - setup_shared_mem(p); - } - memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); - ap_scoreboard_image->global.exit_generation = exit_gen; + if (ap_scoreboard_image == NULL) { + setup_shared_mem(p); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); + ap_scoreboard_image->global.running_generation = running_gen; #else - ap_scoreboard_image = &_scoreboard_image; - ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); + ap_scoreboard_image = &_scoreboard_image; + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); - scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644); - if (scoreboard_fd == -1) { - perror(ap_scoreboard_fname); - fprintf(stderr, "Cannot open scoreboard file:\n"); - exit(APEXIT_INIT); - } - ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup); + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup); - memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image)); - ap_scoreboard_image->global.exit_generation = exit_gen; - force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); + memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image)); + ap_scoreboard_image->global.running_generation = running_gen; + force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); #endif - } } /* Routines called to deal with the scoreboard image @@ -2572,6 +2551,12 @@ deferred_die = 1; } +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +int volatile ap_my_generation; + #ifdef WIN32 /* * Signalling Apache on NT. @@ -3618,7 +3603,7 @@ ap_clear_pool(ptrans); ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.exit_generation >= generation) { + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { clean_child_exit(0); } @@ -3759,7 +3744,7 @@ * without reliable signals */ ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.exit_generation >= generation) { + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { clean_child_exit(0); } } @@ -3864,7 +3849,7 @@ (request_rec *) NULL); ap_sync_scoreboard_image(); - if (ap_scoreboard_image->global.exit_generation >= generation) { + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { ap_bclose(conn_io); clean_child_exit(0); } @@ -3932,6 +3917,16 @@ Explain1("Starting new child in slot %d", slot); (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL); + /* clean up the slot's vhostrec pointer now that it is being re-used, + * and mark the slot as beloging to a new generation. + */ + /* XXX: there's still a race condition here for file-based scoreboards... + * but... like, do we really care to spend yet another write() operation + * here? -djg + */ + ap_scoreboard_image->servers[slot].vhostrec = NULL; + ap_scoreboard_image->parent[slot].generation = ap_my_generation; + #ifndef _OSD_POSIX if ((pid = fork()) == -1) { #else /*_OSD_POSIX*/ @@ -4229,7 +4224,6 @@ ap_standalone = 1; is_graceful = 0; - ++generation; if (!one_process) { detach(); @@ -4262,7 +4256,9 @@ ap_init_modules(pconf, server_conf); version_locked++; /* no more changes to server_version */ SAFE_ACCEPT(accept_mutex_init(pconf)); - reinit_scoreboard(pconf); + if (!is_graceful) { + reinit_scoreboard(pconf); + } #ifdef SCOREBOARD_FILE else { ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname); @@ -4406,14 +4402,16 @@ clean_parent_exit(0); } + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; if (is_graceful) { #ifndef SCOREBOARD_FILE int i; #endif - - /* USE WITH CAUTION: Graceful restarts are not known to work - * in various configurations on the architectures we support. */ - ap_scoreboard_image->global.exit_generation = generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; update_scoreboard_global(); ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, @@ -4446,7 +4444,6 @@ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, "SIGHUP received. Attempting to restart"); } - ++generation; } while (restart_pending); /*add_common_vars(NULL);*/ @@ -5168,7 +5165,7 @@ my_pid = getpid(); - ++generation; + ++ap_my_generation; copy_listeners(pconf); ap_restart_time = time(NULL); @@ -5548,7 +5545,6 @@ processes_to_create = nchild; is_graceful = 0; - ++generation; ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string), "ap%d", getpid()); @@ -5709,7 +5705,7 @@ "SetEvent for child process in slot #%d", i); } } - ++generation; + ++ap_my_generation; } while (restart_pending); /* If we dropped out of the loop we definitly want to die completely. We need to 1.103 +6 -6 apache-1.3/src/modules/standard/mod_status.c Index: mod_status.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_status.c,v retrieving revision 1.102 retrieving revision 1.103 diff -u -r1.102 -r1.103 --- mod_status.c 1998/12/03 14:38:05 1.102 +++ mod_status.c 1998/12/04 19:12:18 1.103 @@ -254,7 +254,7 @@ char stat_buffer[HARD_SERVER_LIMIT]; int pid_buffer[HARD_SERVER_LIMIT]; clock_t tu, ts, tcu, tcs; - char *vhost; + server_rec *vhost; tu = ts = tcu = tcs = 0; @@ -309,10 +309,6 @@ for (i = 0; i < HARD_SERVER_LIMIT; ++i) { score_record = ap_scoreboard_image->servers[i]; ps_record = ap_scoreboard_image->parent[i]; - if (score_record.vhostrec) - vhost = score_record.vhostrec->server_hostname; - else - vhost = "NULL"; res = score_record.status; stat_buffer[i] = status_flags[res]; pid_buffer[i] = (int) ps_record.pid; @@ -492,6 +488,10 @@ for (i = 0; i < HARD_SERVER_LIMIT; ++i) { score_record = ap_scoreboard_image->servers[i]; ps_record = ap_scoreboard_image->parent[i]; + vhost = score_record.vhostrec; + if (ps_record.generation != ap_my_generation) { + vhost = NULL; + } #if defined(NO_GETTIMEOFDAY) #ifndef NO_TIMES @@ -658,7 +658,7 @@ ap_rprintf(r, "<td>%s<td nowrap>%s<td nowrap>%s</tr>\n\n", score_record.client, - (score_record.vhostrec ? vhost : "NULL"), + vhost ? vhost->server_hostname : "(unavailable)", ap_escape_html(r->pool, score_record.request)); } /* no_table_report */ } /* !short_report */