This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[incremental] Patch: FYI: handle interrupts


I'm checking this in on the incremental-compiler branch.

This is the first draft of interrupt support for the server.

The idea is that the server should roughly mimic the command-line user
experience: user types control-c, gcc stops.  Of course, we can't just
SIGINT the server, because we want it to remain running to process
more jobs.

The implementation is to have the client interrupt the server's
process group.  On the server there are two cases:

1. Before forking the back end, the SIGINT handler sets a flag which
   we check "periodically".  In the C front end (the only server-ized
   FE), it is simple to do this in the top-level parsing loop.  For
   most programs, I think this should give acceptable latency.
   However, if not, we can look at adding more checks.

2. After forking the back end, the server is just waiting for its
   subprocess.  Since SIGINT is delivered to the children as well, the
   right thing happens.

Tom

ChangeLog:
2008-01-18  Tom Tromey  <tromey@redhat.com>

	* c-parser.c (c_parser_translation_unit): Check for interrupt.
	Include server.h.
	* server.h (server_interrupted_p): Declare.
	* server.c (server_pid): New global.
	(client_interrupted): Likewise.
	(client_connect): Set server_pid.
	(server_main_loop): Send process id.
	(client_sigint_handler): New function.
	(client_connect): Set SIGINT handler.
	(send_command_and_wait): Remove SIGINT handler.
	(server_start_back_end): Likewise.
	(server_main_loop): Reset 'interrupted'.  Handle SIGINT.
	(server_interrupted_p): New function.
	(server_sigint_handler): Likewise.

Index: server.c
===================================================================
--- server.c	(revision 131637)
+++ server.c	(working copy)
@@ -57,6 +57,12 @@
 /* The file descriptor of our connection to the server.  */
 static int connection_fd = -1;
 
+/* On the client, the process id of the server.  */
+static pid_t server_pid;
+
+/* True if interrupted.  Both the client and the server use this.  */
+static volatile sig_atomic_t interrupted;
+
 /* Return the name of the server socket.  The value is cached in
    'server_socket_name'.  PROGNAME is the path name of the server
    executable and is used to construct the socket name.  If SERVER is
@@ -333,6 +339,13 @@
   return true;
 }
 
+/* On server, the SIGINT handler.  */
+static void
+server_sigint_handler (int ARG_UNUSED (num))
+{
+  interrupted = 1;
+}
+
 /* Main loop of the server.  Creates a server socket and listens to
    it, accepting requests and acting on them.  PROGNAME is the full
    path to the server executable.  FD is the completion file
@@ -345,7 +358,11 @@
   char reply = 't';
   bool result = true;
   int code = SUCCESS_EXIT_CODE;
+  pid_t self = getpid ();
 
+  setsid ();
+  signal (SIGINT, server_sigint_handler);
+
   current_server_state = SERVER_SERVER;
 
   if (sockfd < 0)
@@ -372,8 +389,12 @@
 	  code = FATAL_EXIT_CODE;
 	  break;
 	}
-      result = request_and_response (reqfd);
+
+      if (write (reqfd, &self, sizeof (self)) == sizeof (self))
+	result = request_and_response (reqfd);
       close (reqfd);
+
+      interrupted = 0;
     }
 
   server_cleanup (sockfd);
@@ -382,23 +403,39 @@
 
 /* Fork to let the back end do its work.  Returns true in the parent,
    false in the child.  Will print a message and return true if there
-   is an error.  The parent waits for the child to exit.  */
+   is an error.  The parent waits for the child to exit.  This can
+   also return true, with a false *STATUS, if the server was
+   interrupted.  */
 bool
 server_start_back_end (bool *status)
 {
   pid_t child;
   int result;
+  sigset_t block_int;
 
+  /* Avoid races by blocking SIGINT for a bit.  */
+  sigemptyset (&block_int);
+  sigaddset (&block_int, SIGINT);
+  sigprocmask (SIG_BLOCK, &block_int, NULL);
+
+  if (interrupted)
+    {
+      *status = false;
+      goto done;
+    }
+
   child = fork ();
   if (child == -1)
     {
       error ("fork of server failed: %s", xstrerror (errno));
       *status = false;
-      return true;
+      goto done;
     }
   else if (child == 0)
     {
       /* Child process.  */
+      sigprocmask (SIG_UNBLOCK, &block_int, NULL);
+      signal (SIGINT, SIG_DFL);
       current_server_state = SERVER_CODEGEN;
       return false;
     }
@@ -406,9 +443,33 @@
   /* Parent.  */
   waitpid (child, &result, 0);
   *status = WIFEXITED (result) && ! WEXITSTATUS (result);
+
+ done:
+  /* It is ok to leave SIGINT blocked for the duration of the
+     subprocess, because we will get the correct value in *STATUS
+     anyway.  */
+  sigprocmask (SIG_UNBLOCK, &block_int, NULL);
+
   return true;
 }
 
+/* Return true if the server has received a SIGINT, false otherwise.  */
+bool
+server_interrupted_p (void)
+{
+  return interrupted;
+}
+
+/* Called in response to a SIGINT, while connected to the server.  */
+static void
+client_sigint_handler (int num)
+{
+  gcc_assert (server_pid);
+  interrupted = 1;
+  /* Probably not ok to do this here.  */
+  kill (- server_pid, num);
+}
+
 /* Make a connection to a running server.  PROGNAME is the name of the
    server to connect to.  Returns true on success, false on
    failure.  */
@@ -430,6 +491,12 @@
 	       SUN_LEN (&address)) < 0)
     return false;
 
+  if (read (connection_fd, &server_pid, sizeof (server_pid))
+      != sizeof (server_pid))
+    return false;
+
+  signal (SIGINT, client_sigint_handler);
+
   return true;
 }
 
@@ -539,6 +606,12 @@
 
   if (read (connection_fd, &status, sizeof (status)) != sizeof (status))
     return false;
+
+  /* Make sure we exit with the correct status when interrupted.  */
+  signal (SIGINT, SIG_DFL);
+  if (interrupted)
+    raise (SIGINT);
+
   return status ? false : true;
 }
 
Index: server.h
===================================================================
--- server.h	(revision 131637)
+++ server.h	(working copy)
@@ -24,6 +24,7 @@
 extern void server_start (char *);
 extern int server_main_loop (const char *progname, int);
 extern bool server_start_back_end (bool *);
+extern bool server_interrupted_p (void);
 
 /* Functions for the client to use.  */
 extern bool client_connect (const char *);
Index: c-parser.c
===================================================================
--- c-parser.c	(revision 131545)
+++ c-parser.c	(working copy)
@@ -57,6 +57,7 @@
 #include "target.h"
 #include "cgraph.h"
 #include "md5.h"
+#include "server.h"
 
 
 /* The reserved keyword table.  */
@@ -2085,6 +2086,14 @@
 	     local.  */
 	  size_t next_token = parser->next_token;
 
+	  if (server_interrupted_p ())
+	    {
+	      /* It might be nice to directly add this to diagnostic.h
+		 without having to emit an explicit error message.  */
+	      error ("Interrupted");
+	      break;
+	    }
+
 	  ggc_collect ();
 
 	  /* Skip all the hunks that we've handled.  */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]