Commit af268c62 authored by Alexandre Julliard's avatar Alexandre Julliard

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.
parent 307cb09a
...@@ -53,7 +53,9 @@ ...@@ -53,7 +53,9 @@
static struct list process_list = LIST_INIT(process_list); static struct list process_list = LIST_INIT(process_list);
static int running_processes, user_processes; 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 */ /* process operations */
...@@ -227,17 +229,32 @@ static void set_process_startup_state( struct process *process, enum startup_sta ...@@ -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 */ /* final cleanup once we are sure a process is really dead */
static void process_died( struct process *process ) static void process_died( struct process *process )
{ {
if (debug_level) fprintf( stderr, "%04x: *process killed*\n", process->id ); if (debug_level) fprintf( stderr, "%04x: *process killed*\n", process->id );
if (!process->is_system) if (!process->is_system)
{ {
if (!--user_processes && user_process_event) if (!--user_processes && master_socket_timeout != TIMEOUT_INFINITE)
set_event( user_process_event ); shutdown_timeout = add_timeout_user( master_socket_timeout, server_shutdown_timeout, NULL );
} }
release_object( process ); release_object( process );
if (!--running_processes) close_master_socket(); if (!--running_processes && shutting_down) close_master_socket( 0 );
} }
/* callback for process sigkill timeout */ /* callback for process sigkill timeout */
...@@ -353,7 +370,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit ...@@ -353,7 +370,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
error: error:
if (process) release_object( process ); if (process) release_object( process );
/* if we failed to start our first process, close everything down */ /* 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; return NULL;
} }
...@@ -558,7 +575,7 @@ static void terminate_process( struct process *process, struct thread *skip, int ...@@ -558,7 +575,7 @@ static void terminate_process( struct process *process, struct thread *skip, int
} }
/* kill all processes */ /* 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 (;;) for (;;)
{ {
...@@ -574,6 +591,19 @@ void kill_all_processes( struct process *skip, int exit_code ) ...@@ -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 */ /* kill all processes being attached to a console renderer */
void kill_console_processes( struct thread *renderer, int exit_code ) void kill_console_processes( struct thread *renderer, int exit_code )
{ {
...@@ -634,8 +664,11 @@ void add_process_thread( struct process *process, struct thread *thread ) ...@@ -634,8 +664,11 @@ void add_process_thread( struct process *process, struct thread *thread )
running_processes++; running_processes++;
if (!process->is_system) if (!process->is_system)
{ {
if (!user_processes++ && user_process_event) if (!user_processes++ && shutdown_timeout)
reset_event( user_process_event ); {
remove_timeout_user( shutdown_timeout );
shutdown_timeout = NULL;
}
} }
} }
grab_object( thread ); grab_object( thread );
...@@ -860,6 +893,12 @@ DECL_HANDLER(new_process) ...@@ -860,6 +893,12 @@ DECL_HANDLER(new_process)
close( socket_fd ); close( socket_fd );
return; return;
} }
if (shutting_down)
{
set_error( STATUS_SHUTDOWN_IN_PROGRESS );
close( socket_fd );
return;
}
/* build the startup info for a new process */ /* build the startup info for a new process */
if (!(info = alloc_object( &startup_info_ops ))) return; if (!(info = alloc_object( &startup_info_ops ))) return;
...@@ -1179,19 +1218,20 @@ DECL_HANDLER(make_process_system) ...@@ -1179,19 +1218,20 @@ DECL_HANDLER(make_process_system)
{ {
struct process *process = current->process; 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; if (!(shutdown_event = create_event( NULL, NULL, 0, 1, 0, NULL ))) return;
make_object_static( (struct object *)user_process_event ); 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; return;
if (!process->is_system) if (!process->is_system)
{ {
process->is_system = 1; process->is_system = 1;
close_process_desktop( process ); 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 );
} }
} }
...@@ -121,7 +121,6 @@ extern void remove_process_thread( struct process *process, ...@@ -121,7 +121,6 @@ extern void remove_process_thread( struct process *process,
struct thread *thread ); struct thread *thread );
extern void suspend_process( struct process *process ); extern void suspend_process( struct process *process );
extern void resume_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_process( struct process *process, int violent_death );
extern void kill_console_processes( struct thread *renderer, int exit_code ); extern void kill_console_processes( struct thread *renderer, int exit_code );
extern void kill_debugged_processes( struct thread *debugger, int exit_code ); extern void kill_debugged_processes( struct thread *debugger, int exit_code );
......
...@@ -78,7 +78,6 @@ struct master_socket ...@@ -78,7 +78,6 @@ struct master_socket
{ {
struct object obj; /* object header */ struct object obj; /* object header */
struct fd *fd; /* file descriptor of the master socket */ 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 ); 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 ...@@ -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 */ timeout_t server_start_time = 0; /* server startup time */
static struct master_socket *master_socket; /* the master socket object */ static struct master_socket *master_socket; /* the master socket object */
static int force_shutdown; static struct timeout_user *master_timeout;
/* socket communication static structures */ /* socket communication static structures */
static struct iovec myiovec; static struct iovec myiovec;
...@@ -508,11 +507,6 @@ static void master_socket_poll_event( struct fd *fd, int event ) ...@@ -508,11 +507,6 @@ static void master_socket_poll_event( struct fd *fd, int event )
unsigned int len = sizeof(dummy); unsigned int len = sizeof(dummy);
int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len ); int client = accept( get_unix_fd( master_socket->fd ), (struct sockaddr *) &dummy, &len );
if (client == -1) return; if (client == -1) return;
if (sock->timeout)
{
remove_timeout_user( sock->timeout );
sock->timeout = NULL;
}
fcntl( client, F_SETFL, O_NONBLOCK ); fcntl( client, F_SETFL, O_NONBLOCK );
create_process( client, NULL, 0 ); create_process( client, NULL, 0 );
} }
...@@ -731,7 +725,6 @@ static void acquire_lock(void) ...@@ -731,7 +725,6 @@ static void acquire_lock(void)
if (!(master_socket = alloc_object( &master_socket_ops )) || if (!(master_socket = alloc_object( &master_socket_ops )) ||
!(master_socket->fd = create_anonymous_fd( &master_socket_fd_ops, fd, &master_socket->obj, 0 ))) !(master_socket->fd = create_anonymous_fd( &master_socket_fd_ops, fd, &master_socket->obj, 0 )))
fatal_error( "out of memory\n" ); fatal_error( "out of memory\n" );
master_socket->timeout = NULL;
set_fd_events( master_socket->fd, POLLIN ); set_fd_events( master_socket->fd, POLLIN );
make_object_static( &master_socket->obj ); make_object_static( &master_socket->obj );
} }
...@@ -810,40 +803,29 @@ void open_master_socket(void) ...@@ -810,40 +803,29 @@ void open_master_socket(void)
/* master socket timer expiration handler */ /* master socket timer expiration handler */
static void close_socket_timeout( void *arg ) static void close_socket_timeout( void *arg )
{ {
master_socket->timeout = NULL; master_timeout = NULL;
flush_registry(); 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() ); if (debug_level) fprintf( stderr, "wineserver: exiting (pid=%ld)\n", (long) getpid() );
#ifdef DEBUG_OBJECTS #ifdef DEBUG_OBJECTS
close_objects(); /* shut down everything properly */ close_objects(); /* shut down everything properly */
#endif #endif
exit( force_shutdown ); exit( 0 );
} }
/* close the master socket and stop waiting for new clients */ /* 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; if (master_socket)
master_socket_timeout = 0;
if (master_socket->timeout)
{ {
remove_timeout_user( master_socket->timeout ); release_object( master_socket );
close_socket_timeout( NULL ); 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 );
} }
...@@ -57,7 +57,7 @@ extern void read_request( struct thread *thread ); ...@@ -57,7 +57,7 @@ extern void read_request( struct thread *thread );
extern void write_reply( struct thread *thread ); extern void write_reply( struct thread *thread );
extern unsigned int get_tick_count(void); extern unsigned int get_tick_count(void);
extern void open_master_socket(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 void shutdown_master_socket(void);
extern int wait_for_lock(void); extern int wait_for_lock(void);
extern int kill_lock_owner( int sig ); extern int kill_lock_owner( int sig );
......
...@@ -190,8 +190,6 @@ static void sigterm_callback(void) ...@@ -190,8 +190,6 @@ static void sigterm_callback(void)
/* SIGINT callback */ /* SIGINT callback */
static void sigint_callback(void) static void sigint_callback(void)
{ {
kill_all_processes( NULL, 1 );
flush_registry();
shutdown_master_socket(); shutdown_master_socket();
} }
......
...@@ -4546,6 +4546,7 @@ static const struct ...@@ -4546,6 +4546,7 @@ static const struct
{ "SECTION_TOO_BIG", STATUS_SECTION_TOO_BIG }, { "SECTION_TOO_BIG", STATUS_SECTION_TOO_BIG },
{ "SEMAPHORE_LIMIT_EXCEEDED", STATUS_SEMAPHORE_LIMIT_EXCEEDED }, { "SEMAPHORE_LIMIT_EXCEEDED", STATUS_SEMAPHORE_LIMIT_EXCEEDED },
{ "SHARING_VIOLATION", STATUS_SHARING_VIOLATION }, { "SHARING_VIOLATION", STATUS_SHARING_VIOLATION },
{ "SHUTDOWN_IN_PROGRESS", STATUS_SHUTDOWN_IN_PROGRESS },
{ "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED }, { "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED },
{ "THREAD_IS_TERMINATING", STATUS_THREAD_IS_TERMINATING }, { "THREAD_IS_TERMINATING", STATUS_THREAD_IS_TERMINATING },
{ "TIMEOUT", STATUS_TIMEOUT }, { "TIMEOUT", STATUS_TIMEOUT },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment