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.
This commit is contained in:
parent
307cb09a62
commit
af268c6211
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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 )
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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
|
||||||
{
|
{
|
||||||
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
|
||||||
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 )
|
||||||
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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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 );
|
||||||
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)
|
||||||
/* 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
|
||||||
{ "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 },
|
||||||
|
|
Loading…
Reference in New Issue