On Sat, Sep 14, 2002 at 12:17:53PM +0200, Poul-Henning Kamp wrote:
> 
> This is just to note that I have updated the JKH page with a lot of new
> stuff, so if your coding-pencil itches:
> 
>       http://people.freebsd.org/~phk/TODO/

|Make -j improvement
|
|make(1) with -j option uses a select loop to wait for events, and every
|100msec it drops out to look for processes exited etc.  A pure "make
|buildworld" on a single-CPU machine is up to 25% faster that the best
|"make -j N buildworld" time on the same hardware.  Changing to timeout
|to be 10msec improves things about 10%.
|I think that make(1) should use kqueue(2) instead, since that would
|eliminate the need for timeouts.

Ok, here's what I came up with.  However, with the patch applied, each
'make buildworld' on a SMP machine throws tons of

/freebsd/current/src/sys/vm/uma_core.c:1307: could sleep with "filedesc structure" 
locked from /freebsd/current/src/sys/kern/kern_event.c:959

at me and freezes badly at some point (no breaking into ddb possible).
This is totally repeatable.  Is anybody able to reproduce (and maybe
fix) this?

Regards,
Stefan Farfeleder
Index: job.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/make/job.c,v
retrieving revision 1.43
diff -u -r1.43 job.c
--- job.c       29 Sep 2002 00:20:28 -0000      1.43
+++ job.c       2 Oct 2002 21:34:51 -0000
@@ -64,9 +64,8 @@
  *     Job_CatchOutput         Print any output our children have produced.
  *                             Should also be called fairly frequently to
  *                             keep the user informed of what's going on.
- *                             If no output is waiting, it will block for
- *                             a time given by the SEL_* constants, below,
- *                             or until output is ready.
+ *                             If no output is waiting, it will block until
+ *                             a child terminates or until output is ready.
  *
  *     Job_Init                Called to intialize this module. in addition,
  *                             any commands attached to the .BEGIN target
@@ -107,6 +106,8 @@
 #include <sys/file.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <sys/event.h>
+#include <sys/time.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -237,8 +238,7 @@
                                 * (2) a job can only be run locally, but
                                 * nLocal equals maxLocal */
 #ifndef RMT_WILL_WATCH
-static fd_set          outputs;        /* Set of descriptors of pipes connected to
-                                * the output channels of children */
+static int     kqfd;           /* File descriptor obtained by kqueue() */
 #endif
 
 STATIC GNode           *lastNode;      /* The node for which output was most recently
@@ -692,8 +692,6 @@
     if (usePipes) {
 #ifdef RMT_WILL_WATCH
        Rmt_Ignore(job->inPipe);
-#else
-       FD_CLR(job->inPipe, &outputs);
 #endif
        if (job->outPipe != job->inPipe) {
           (void) close(job->outPipe);
@@ -1265,14 +1263,25 @@
            /*
             * The first time a job is run for a node, we set the current
             * position in the buffer to the beginning and mark another
-            * stream to watch in the outputs mask
+            * stream to watch in the outputs mask.  The termination of this
+            * job and the availability of new data in the pipe are
+            * registered in the kqueue.
             */
+           struct kevent       kev[2];
+
            job->curPos = 0;
 
 #ifdef RMT_WILL_WATCH
            Rmt_Watch(job->inPipe, JobLocalInput, job);
 #else
-           FD_SET(job->inPipe, &outputs);
+           EV_SET(&kev[0], job->inPipe, EVFILT_READ, EV_ADD, 0, 0, job);
+           EV_SET(&kev[1], job->pid, EVFILT_PROC, EV_ADD | EV_ONESHOT,
+               NOTE_EXIT, 0, NULL);
+           if (kevent(kqfd, kev, 2, NULL, 0, NULL) != 0) {
+               /* kevent() will fail if the job is already finished */
+               if (errno != EBADF && errno != ESRCH)
+                   Punt("kevent: %s", strerror(errno));
+           }
 #endif /* RMT_WILL_WATCH */
        }
 
@@ -2229,12 +2238,12 @@
 Job_CatchOutput()
 {
     int                  nfds;
-    struct timeval       timeout;
-    fd_set               readfds;
-    LstNode              ln;
-    Job                          *job;
 #ifdef RMT_WILL_WATCH
     int                          pnJobs;       /* Previous nJobs */
+#else
+    struct kevent        kev[4];       /* 4 is somewhat arbitrary */
+    size_t               kevsize = sizeof(kev) / sizeof(kev[0]);
+    int                          i;
 #endif
 
     (void) fflush(stdout);
@@ -2262,25 +2271,20 @@
     }
 #else
     if (usePipes) {
-       readfds = outputs;
-       timeout.tv_sec = SEL_SEC;
-       timeout.tv_usec = SEL_USEC;
-
-       if ((nfds = select(FD_SETSIZE, &readfds, (fd_set *) 0,
-                          (fd_set *) 0, &timeout)) <= 0)
-           return;
-       else {
-           if (Lst_Open(jobs) == FAILURE) {
-               Punt("Cannot open job table");
-           }
-           while (nfds && (ln = Lst_Next(jobs)) != NULL) {
-               job = (Job *) Lst_Datum(ln);
-               if (FD_ISSET(job->inPipe, &readfds)) {
-                   JobDoOutput(job, FALSE);
-                   nfds -= 1;
+       if ((nfds = kevent(kqfd, NULL, 0, kev, kevsize, NULL)) == -1) {
+           Punt("kevent: %s", strerror(errno));
+       } else {
+           for (i = 0; i < nfds; i++) {
+               switch (kev[i].filter) {
+               case EVFILT_READ:
+                   JobDoOutput(kev[i].udata, FALSE);
+                   break;
+               case EVFILT_PROC:
+                   /* Just wake up and let Job_CatchChildren() collect the
+                    * terminated job. */
+                   break;
                }
            }
-           Lst_Close(jobs);
        }
     }
 #endif /* RMT_WILL_WATCH */
@@ -2408,6 +2412,13 @@
     }
     if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) {
        (void) signal(SIGWINCH, JobPassSig);
+    }
+#endif
+
+#ifndef RMT_WILL_WATCH
+    
+    if ((kqfd = kqueue()) == -1) {
+       Punt("kqueue: %s", strerror(errno));
     }
 #endif
 
Index: job.h
===================================================================
RCS file: /home/ncvs/src/usr.bin/make/job.h,v
retrieving revision 1.18
diff -u -r1.18 job.h
--- job.h       26 Sep 2002 01:39:22 -0000      1.18
+++ job.h       1 Oct 2002 16:31:10 -0000
@@ -50,14 +50,6 @@
 
 #define        TMPPAT  "/tmp/makeXXXXXXXXXX"
 
-/*
- * The SEL_ constants determine the maximum amount of time spent in select
- * before coming out to see if a child has finished. SEL_SEC is the number of
- * seconds and SEL_USEC is the number of micro-seconds
- */
-#define        SEL_SEC         0
-#define        SEL_USEC        100000
-
 
 /*-
  * Job Table definitions.

Reply via email to