make-alpha
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: GNU make jobserver redux


From: Paul D. Smith
Subject: Re: GNU make jobserver redux
Date: Sat, 5 May 2001 15:55:28 -0400

%% Paul Eggert <address@hidden> writes:

  >> That is, once you start the read() with a valid FD, even if the signal
  >> handler closes that FD then the read() still continues and does not
  >> return with EBADF.

  pe> Hmm, is this why you wanted that to enable EINTR-style reading
  pe> just for that one read(), and disable it for all other system
  pe> calls?  If so, then it shouldn't be hard to modify my suggestion
  pe> along those lines.  This will still avoid having the other code
  pe> worry about EINTR; only that one read() would have to worry about
  pe> it.

  pe> Perhaps this is what you were suggesting anyway, and I didn't
  pe> fully understand your suggestion.  Sorry; it is taking me a bit of
  pe> time to remember all the issues involved.

Yes, this is what I was suggesting.  That instead of installing the
SIGCHLD handler at the start of the make process and leaving it there,
we only install it when we're going to do the read, then after we get
the token we put back SIG_DFL for SIGCHLD... this should mean that the
only place we have to worry about interrupted system calls is for that
block of code, which is well-defined.

  >> I believe I wrote a test program for this and we passed it around via
  >> email, and concluded that some fairly standard UNIX implementations also
  >> had this behavior.

  pe> I recall doing the test, but I don't recall the program.
  pe> Can you send me a copy again?

Enclosed below.  I actually tried this on Linux and Solaris and it
worked correctly; however, if it doesn't work on Hurd I consider that a
problem.

The code here is obviously much more complicated than needed to just
test this feature, but it does a reasonably good job of emulating the
processing that make actually does WRT jobserver.

  >> There is one problem with removing the EINTR handling: on some very
  >> old systems we do not have any way to set restartable syscalls (as
  >> I understand it).

  pe> Yes, some ancient systems had that property.  I doubt whether any
  pe> are still in active use by GNU make users, though.  As you
  pe> mention, even GNU make 3.79.1 doesn't work reliably on those hosts
  pe> anyway (the comment in front of child_handler says the same
  pe> thing).  I wouldn't let those old hosts significantly affect
  pe> "make" development any more.

Yep, I think I agree with you.


#include <stdio.h>
#include <signal.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int jpipe[2];
int rdup;

void
handler(int s)
{
  printf ("Got a sigchld!\n");
  close (rdup);
}

/* Here ebadf serves double-duty: if it's 1 then we got here via EBADF so we
   don't want to block and we want to reap until there are no more children
   ready to be reaped right now.  If it's 0 then we want to block until we've
   reaped all children we have.  */
void
reap_children(int ebadf)
{
  char token = 'x';

  printf("Entered reap_children EBADF = %d\n", ebadf);

  /* If we got here due to EBADF, first re-dup the pipe.  */
  if (ebadf)
    if ((rdup = dup (jpipe[0])) < 0)
      perror ("reap dup"), exit(1);

  /* Now loop grabbing children.  */
  for (;;)
    {
      int status;
      pid_t p = waitpid (-1, &status, ebadf ? WNOHANG : 0);

      switch (p)
        {
        case -1:
          switch (errno)
            {
            case EINTR:
              continue;
            case ECHILD:
              return;
            default:
              perror ("waitpid");
              exit (1);
            }
          break;

        case 0:
          printf ("No kids!\n");
          return;

        default:
          printf ("PID %d died\n", p);
          sleep (1);

          while (write (jpipe[1], &token, 1) != 1)
            if (errno != EINTR)
              perror ("write"), exit (1);

          break;
        }
    }
}


void
start_child()
{
  pid_t p;
  char token;
  int r;

  /* Get a token for this child.  */
  while (read (rdup, &token, 1) != 1)
    if (errno == EBADF)
      reap_children (1);
    else if (errno != EINTR) {
      perror ("read");
      exit (1);
    }

  p = fork();
  switch (p)
    {
    case -1:
      perror ("fork");
      exit (1);

    case 0:
      execl("/bin/sh", "/bin/sh", "-c", "sleep 3", (char *)0);
      _exit (1);

    default:
      printf ("Started PID %d\n", p);
      break;
    }
}

int
main(int argc, char **argv)
{
  struct sigaction sa = {0};
  char token = 'x';
  int n, j;

  if (argc != 3)
    fprintf (stderr, "%s: usage: %s j n\n", *argv, *argv), exit (1);

  srandom (getpid());

  sa.sa_handler = handler;
  sa.sa_flags = SA_RESTART;
  sigaction (SIGCHLD, &sa, NULL);

  if (pipe (jpipe))
    return perror ("pipe"), 1;

  if ((rdup = dup (jpipe[0])) < 0)
    return perror ("dup"), 1;

  j = atoi (argv[1]);
  n = atoi (argv[2]);

  /* Give enough tokens for J jobs.  */
  while (j--)
    if (write (jpipe[1], &token, 1) != 1)
      return perror ("write"), 1;

  /* Now start N kids. */
  while (n--)
    start_child ();

  reap_children (0);

  return 0;
}

-- 
-------------------------------------------------------------------------------
 Paul D. Smith <address@hidden>          Find some GNU make tips at:
 http://www.gnu.org                      http://www.paulandlesley.org/gmake/
 "Please remain calm...I may be mad, but I am a professional." --Mad Scientist

reply via email to

[Prev in Thread] Current Thread [Next in Thread]