From af268c621131624fc34f34ccab9fcd8d47500c31 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard <julliard@winehq.org> Date: Wed, 2 Jan 2008 16:16:00 +0100 Subject: [PATCH] server: Redesign the server shutdown processing. System processes are now killed only after the server persistence delay has expired. New processes are not allowed to start during shutdown. --- server/process.c | 66 ++++++++++++++++++++++++++++++++++++++---------- server/process.h | 1 - server/request.c | 46 ++++++++++----------------------- server/request.h | 2 +- server/signal.c | 2 -- server/trace.c | 1 + 6 files changed, 69 insertions(+), 49 deletions(-) diff --git a/server/process.c b/server/process.c index a78eac4d865..c6f34ec0db0 100644 --- a/server/process.c +++ b/server/process.c @@ -53,7 +53,9 @@ static struct list process_list = LIST_INIT(process_list); static int running_processes, user_processes; -static struct event *user_process_event; /* signaled when all user processes have exited */ +static struct event *shutdown_event; /* signaled when shutdown starts */ +static struct timeout_user *shutdown_timeout; /* timeout for server shutdown */ +static int shutting_down; /* are we in the process of shutting down the server? */ /* process operations */ @@ -227,17 +229,32 @@ static void set_process_startup_state( struct process *process, enum startup_sta } } +/* callback for server shutdown */ +static void server_shutdown_timeout( void *arg ) +{ + shutdown_timeout = NULL; + if (!running_processes) close_master_socket( 0 ); + else + { + if (debug_level) fprintf( stderr, "wineserver: shutting down\n" ); + if (shutdown_event) set_event( shutdown_event ); + /* leave 2 seconds for system processes to exit */ + close_master_socket( 2 * -TICKS_PER_SEC ); + shutting_down = 1; + } +} + /* final cleanup once we are sure a process is really dead */ static void process_died( struct process *process ) { if (debug_level) fprintf( stderr, "%04x: *process killed*\n", process->id ); if (!process->is_system) { - if (!--user_processes && user_process_event) - set_event( user_process_event ); + if (!--user_processes && master_socket_timeout != TIMEOUT_INFINITE) + shutdown_timeout = add_timeout_user( master_socket_timeout, server_shutdown_timeout, NULL ); } release_object( process ); - if (!--running_processes) close_master_socket(); + if (!--running_processes && shutting_down) close_master_socket( 0 ); } /* callback for process sigkill timeout */ @@ -353,7 +370,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit error: if (process) release_object( process ); /* if we failed to start our first process, close everything down */ - if (!running_processes) close_master_socket(); + if (!running_processes) close_master_socket( 0 ); return NULL; } @@ -558,7 +575,7 @@ static void terminate_process( struct process *process, struct thread *skip, int } /* kill all processes */ -void kill_all_processes( struct process *skip, int exit_code ) +static void kill_all_processes( struct process *skip, int exit_code ) { for (;;) { @@ -574,6 +591,19 @@ void kill_all_processes( struct process *skip, int exit_code ) } } +/* forced shutdown, used for wineserver -k */ +void shutdown_master_socket(void) +{ + kill_all_processes( NULL, 1 ); + master_socket_timeout = 0; + if (shutdown_timeout) + { + remove_timeout_user( shutdown_timeout ); + shutdown_timeout = NULL; + } + if (!shutting_down) server_shutdown_timeout( NULL ); +} + /* kill all processes being attached to a console renderer */ void kill_console_processes( struct thread *renderer, int exit_code ) { @@ -634,8 +664,11 @@ void add_process_thread( struct process *process, struct thread *thread ) running_processes++; if (!process->is_system) { - if (!user_processes++ && user_process_event) - reset_event( user_process_event ); + if (!user_processes++ && shutdown_timeout) + { + remove_timeout_user( shutdown_timeout ); + shutdown_timeout = NULL; + } } } grab_object( thread ); @@ -860,6 +893,12 @@ DECL_HANDLER(new_process) close( socket_fd ); return; } + if (shutting_down) + { + set_error( STATUS_SHUTDOWN_IN_PROGRESS ); + close( socket_fd ); + return; + } /* build the startup info for a new process */ if (!(info = alloc_object( &startup_info_ops ))) return; @@ -1179,19 +1218,20 @@ DECL_HANDLER(make_process_system) { struct process *process = current->process; - if (!user_process_event) + if (!shutdown_event) { - if (!(user_process_event = create_event( NULL, NULL, 0, 1, 0, NULL ))) return; - make_object_static( (struct object *)user_process_event ); + if (!(shutdown_event = create_event( NULL, NULL, 0, 1, 0, NULL ))) return; + make_object_static( (struct object *)shutdown_event ); } - if (!(reply->event = alloc_handle( current->process, user_process_event, SYNCHRONIZE, 0 ))) + if (!(reply->event = alloc_handle( current->process, shutdown_event, SYNCHRONIZE, 0 ))) return; if (!process->is_system) { process->is_system = 1; close_process_desktop( process ); - if (!--user_processes) set_event( user_process_event ); + if (!--user_processes && master_socket_timeout != TIMEOUT_INFINITE) + shutdown_timeout = add_timeout_user( master_socket_timeout, server_shutdown_timeout, NULL ); } } diff --git a/server/process.h b/server/process.h index 30549433472..dbd7dacf4a8 100644 --- a/server/process.h +++ b/server/process.h @@ -121,7 +121,6 @@ extern void remove_process_thread( struct process *process, struct thread *thread ); extern void suspend_process( struct process *process ); extern void resume_process( struct process *process ); -extern void kill_all_processes( struct process *skip, int exit_code ); extern void kill_process( struct process *process, int violent_death ); extern void kill_console_processes( struct thread *renderer, int exit_code ); extern void kill_debugged_processes( struct thread *debugger, int exit_code ); diff --git a/server/request.c b/server/request.c index 759549ae92b..7839de786bf 100644 --- a/server/request.c +++ b/server/request.c @@ -78,7 +78,6 @@ struct master_socket { struct object obj; /* object header */ struct fd *fd; /* file descriptor of the master socket */ - struct timeout_user *timeout; /* timeout on last process exit */ }; static void master_socket_dump( struct object *obj, int verbose ); @@ -123,7 +122,7 @@ unsigned int global_error = 0; /* global error code for when no thread is curre timeout_t server_start_time = 0; /* server startup time */ static struct master_socket *master_socket; /* the master socket object */ -static int force_shutdown; +static struct timeout_user *master_timeout; /* socket communication static structures */ static struct iovec myiovec; @@ -508,11 +507,6 @@ static void master_socket_poll_event( struct fd *fd, int event ) unsigned int len = sizeof(dummy); int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len ); if (client == -1) return; - if (sock->timeout) - { - remove_timeout_user( sock->timeout ); - sock->timeout = NULL; - } fcntl( client, F_SETFL, O_NONBLOCK ); create_process( client, NULL, 0 ); } @@ -731,7 +725,6 @@ static void acquire_lock(void) if (!(master_socket = alloc_object( &master_socket_ops )) || !(master_socket->fd = create_anonymous_fd( &master_socket_fd_ops, fd, &master_socket->obj, 0 ))) fatal_error( "out of memory\n" ); - master_socket->timeout = NULL; set_fd_events( master_socket->fd, POLLIN ); make_object_static( &master_socket->obj ); } @@ -810,40 +803,29 @@ void open_master_socket(void) /* master socket timer expiration handler */ static void close_socket_timeout( void *arg ) { - master_socket->timeout = NULL; + master_timeout = NULL; flush_registry(); - - /* if a new client is waiting, we keep on running */ - if (!force_shutdown && check_fd_events( master_socket->fd, POLLIN )) return; - if (debug_level) fprintf( stderr, "wineserver: exiting (pid=%ld)\n", (long) getpid() ); #ifdef DEBUG_OBJECTS close_objects(); /* shut down everything properly */ #endif - exit( force_shutdown ); + exit( 0 ); } /* close the master socket and stop waiting for new clients */ -void close_master_socket(void) +void close_master_socket( timeout_t timeout ) { - if (master_socket_timeout == TIMEOUT_INFINITE) return; /* just keep running forever */ - - if (master_socket_timeout) - master_socket->timeout = add_timeout_user( master_socket_timeout, close_socket_timeout, NULL ); - else - close_socket_timeout( NULL ); /* close it right away */ -} - -/* forced shutdown, used for wineserver -k */ -void shutdown_master_socket(void) -{ - force_shutdown = 1; - master_socket_timeout = 0; - if (master_socket->timeout) + if (master_socket) { - remove_timeout_user( master_socket->timeout ); - close_socket_timeout( NULL ); + release_object( master_socket ); + master_socket = NULL; } - set_fd_events( master_socket->fd, -1 ); /* stop waiting for new clients */ + if (master_timeout) /* cancel previous timeout */ + remove_timeout_user( master_timeout ); + + if (timeout) + master_timeout = add_timeout_user( timeout, close_socket_timeout, NULL ); + else /* close it right away */ + close_socket_timeout( NULL ); } diff --git a/server/request.h b/server/request.h index 6c5f14664ed..3f035db46db 100644 --- a/server/request.h +++ b/server/request.h @@ -57,7 +57,7 @@ extern void read_request( struct thread *thread ); extern void write_reply( struct thread *thread ); extern unsigned int get_tick_count(void); extern void open_master_socket(void); -extern void close_master_socket(void); +extern void close_master_socket( timeout_t timeout ); extern void shutdown_master_socket(void); extern int wait_for_lock(void); extern int kill_lock_owner( int sig ); diff --git a/server/signal.c b/server/signal.c index 5bcd9cbe908..5e4fe33c6a3 100644 --- a/server/signal.c +++ b/server/signal.c @@ -190,8 +190,6 @@ static void sigterm_callback(void) /* SIGINT callback */ static void sigint_callback(void) { - kill_all_processes( NULL, 1 ); - flush_registry(); shutdown_master_socket(); } diff --git a/server/trace.c b/server/trace.c index 47e059ebb93..8a110668ebf 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4546,6 +4546,7 @@ static const struct { "SECTION_TOO_BIG", STATUS_SECTION_TOO_BIG }, { "SEMAPHORE_LIMIT_EXCEEDED", STATUS_SEMAPHORE_LIMIT_EXCEEDED }, { "SHARING_VIOLATION", STATUS_SHARING_VIOLATION }, + { "SHUTDOWN_IN_PROGRESS", STATUS_SHUTDOWN_IN_PROGRESS }, { "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED }, { "THREAD_IS_TERMINATING", STATUS_THREAD_IS_TERMINATING }, { "TIMEOUT", STATUS_TIMEOUT },