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 },