[BACK]Return to job.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Diff for /src/usr.bin/make/job.c between version 1.111 and 1.112

version 1.111, 2008/01/12 13:08:59 version 1.112, 2008/01/29 22:23:10
Line 161 
Line 161 
 static LIST     runningJobs;    /* The structures that describe them */  static LIST     runningJobs;    /* The structures that describe them */
 static bool     jobFull;        /* Flag to tell when the job table is full. It  static bool     jobFull;        /* Flag to tell when the job table is full. It
                                  * is set true when nJobs equals maxJobs */                                   * is set true when nJobs equals maxJobs */
 static fd_set   *outputsp;      /* Set of descriptors of pipes connected to  
                                  * the output channels of children */  
 static int      outputsn;  
 static GNode    *lastNode;      /* The node for which output was most recently  static GNode    *lastNode;      /* The node for which output was most recently
                                  * produced. */                                   * produced. */
   
   /* data structure linked to job handling through select */
   static fd_set *output_mask = NULL;      /* File descriptors to look for */
   
   static fd_set *actual_mask = NULL;      /* actual select argument */
   static int largest_fd = -1;
   static size_t mask_size = 0;
   
 /*  /*
  * When JobStart attempts to run a job but isn't allowed to,   * When JobStart attempts to run a job but isn't allowed to,
  * the job is placed on the queuedJobs queue to be run   * the job is placed on the queuedJobs queue to be run
Line 173 
Line 178 
  */   */
 static LIST     stoppedJobs;  static LIST     stoppedJobs;
 static LIST     queuedJobs;  static LIST     queuedJobs;
   
   /* wait possibilities */
   #define JOB_EXITED 0
   #define JOB_SIGNALED 1
   #define JOB_CONTINUED 2
   #define JOB_STOPPED 3
   #define JOB_UNKNOWN 4
   
 static LIST     errorsList;  static LIST     errorsList;
 static int      errors;  static int      errors;
 struct error_info {  struct error_info {
         int status;          int reason;
           int code;
         GNode *n;          GNode *n;
 };  };
   
   
   
 #if defined(USE_PGRP) && defined(SYSV)  #if defined(USE_PGRP) && defined(SYSV)
 # define KILL(pid, sig)         killpg(-(pid), (sig))  # define KILL(pid, sig)         killpg(-(pid), (sig))
 #else  #else
Line 192 
Line 205 
 # endif  # endif
 #endif  #endif
   
 /*  
  * Grmpf... There is no way to set bits of the wait structure  
  * anymore with the stupid W*() macros. I liked the union wait  
  * stuff much more. So, we devise our own macros... This is  
  * really ugly, use dramamine sparingly. You have been warned.  
  */  
 #define W_SETMASKED(st, val, fun)                               \  
         {                                                       \  
                 int sh = (int) ~0;                              \  
                 int mask = fun(sh);                             \  
                                                                 \  
                 for (sh = 0; ((mask >> sh) & 1) == 0; sh++)     \  
                         continue;                               \  
                 *(st) = (*(st) & ~mask) | ((val) << sh);        \  
         }  
   
 #define W_SETTERMSIG(st, val) W_SETMASKED(st, val, WTERMSIG)  
 #define W_SETEXITSTATUS(st, val) W_SETMASKED(st, val, WEXITSTATUS)  
   
   
 static void pass_signal_to_job(void *, void *);  static void pass_signal_to_job(void *, void *);
 static void handle_all_signals(void);  static void handle_all_signals(void);
 static void handle_signal(int);  static void handle_signal(int);
 static int JobCmpPid(void *, void *);  static int JobCmpPid(void *, void *);
 static void JobClose(Job *);  
 static void JobFinish(Job *, int);  static void JobFinish(Job *, int);
   static void finish_job(Job *, int, int);
 static void JobExec(Job *);  static void JobExec(Job *);
 static void JobRestart(Job *);  static void JobRestart(Job *);
 static void JobStart(GNode *, int);  static void JobStart(GNode *, int);
Line 227 
Line 220 
 static Job *prepare_job(GNode *, int);  static Job *prepare_job(GNode *, int);
 static void start_queued_job(Job *);  static void start_queued_job(Job *);
 static void banner(Job *, FILE *);  static void banner(Job *, FILE *);
   
   /***
    ***  Input/output from jobs
    ***/
   
   /* prepare_pipe(jp, &fd):
    *      set up pipe data structure (buffer and pos) corresponding to
    *      pointed fd, and prepare to watch for it.
    */
   static void prepare_pipe(struct job_pipe *, int *);
   
   /* close_job_pipes(j):
    *      handle final output from job, and close pipes properly
    */
   static void close_job_pipes(Job *);
   
   
   static void handle_all_jobs_output(void);
   
   /* handle_job_output(job, n, finish):
    *      n = 0 or 1 (stdout/stderr), set finish to retrieve everything.
    */
   static void handle_job_output(Job *, int, bool);
   
 static void print_partial_buffer(struct job_pipe *, Job *, FILE *, size_t);  static void print_partial_buffer(struct job_pipe *, Job *, FILE *, size_t);
 static void print_partial_buffer_and_shift(struct job_pipe *, Job *, FILE *,  static void print_partial_buffer_and_shift(struct job_pipe *, Job *, FILE *,
     size_t);      size_t);
 static bool print_complete_lines(struct job_pipe *, Job *, FILE *, size_t);  static bool print_complete_lines(struct job_pipe *, Job *, FILE *, size_t);
 static void prepare_pipe(struct job_pipe *, int *);  
 static void handle_job_output(Job *, int, bool);  
 static void register_error(int, Job *);  static void register_error(int, int, Job *);
 static void loop_handle_running_jobs(void);  static void loop_handle_running_jobs(void);
 static void Job_CatchChildren(void);  static void Job_CatchChildren(void);
 static void Job_CatchOutput(void);  
   
 static void  static void
 register_error(int status, Job *job)  register_error(int reason, int code, Job *job)
 {  {
         struct error_info *p;          struct error_info *p;
   
         errors++;          errors++;
         p = emalloc(sizeof(struct error_info));          p = emalloc(sizeof(struct error_info));
         p->status = status;          p->reason = reason;
           p->code = code;
         p->n = job->node;          p->n = job->node;
         Lst_AtEnd(&errorsList, p);          Lst_AtEnd(&errorsList, p);
 }  }
Line 256 
Line 273 
         LstNode ln;          LstNode ln;
         struct error_info *p;          struct error_info *p;
         const char *type;          const char *type;
         int r;  
   
         for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) {          for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) {
                 p = (struct error_info *)Lst_Datum(ln);                  p = (struct error_info *)Lst_Datum(ln);
                 if (WIFEXITED(p->status)) {                  switch(p->reason) {
                   case JOB_EXITED:
                         type = "Exit status";                          type = "Exit status";
                         r = WEXITSTATUS(p->status);                          break;
                 } else if (WIFSIGNALED(p->status)) {                  case JOB_SIGNALED:
                         type = "Received signal";                          type = "Received signal";
                         r = WTERMSIG(p->status);                          break;
                 } else {                  case JOB_STOPPED:
                         type = "Status";                          type = "Stopped";
                         r = p->status;                          break;
                   case JOB_CONTINUED:
                           type = "Continued";
                           break;
                   default:
                           type = "Should not happen";
                           break;
                 }                  }
         if (p->n->lineno)          if (p->n->lineno)
                 Error(" %s %d (%s, line %lu of %s)",                  Error(" %s %d (%s, line %lu of %s)",
                     type, r, p->n->name, p->n->lineno, p->n->fname);                      type, p->code, p->n->name, p->n->lineno, p->n->fname);
         else          else
                 Error(" %s %d (%s)", type, r, p->n->name);                  Error(" %s %d (%s)", type, p->code, p->n->name);
         }          }
 }  }
   
Line 461 
Line 484 
         }          }
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  
  * JobClose --  
  *      Called to close both input and output pipes when a job is finished.  
  *  
  * Side Effects:  
  *      The file descriptors associated with the job are closed.  
  *-----------------------------------------------------------------------  
  */  
 static void  static void
 JobClose(Job *job)  close_job_pipes(Job *job)
 {  {
         int i;          int i;
   
         for (i = 0; i < 2; i++) {          for (i = 1; i >= 0; i--) {
                 FD_CLR(job->in[i].fd, outputsp);                  FD_CLR(job->in[i].fd, output_mask);
                 handle_job_output(job, i, true);                  handle_job_output(job, i, true);
                 (void)close(job->in[i].fd);                  (void)close(job->in[i].fd);
         }          }
Line 499 
Line 513 
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 /*ARGSUSED*/  /*ARGSUSED*/
   
 static void  static void
 JobFinish(Job *job, int status)  JobFinish(Job *job, int status)
 {  {
           int reason, code;
           /* parse status */
           if (WIFEXITED(status)) {
                   reason = JOB_EXITED;
                   code = WEXITSTATUS(status);
           } else if (WIFSIGNALED(status)) {
                   reason = JOB_SIGNALED;
                   code = WTERMSIG(status);
           } else if (WIFCONTINUED(status)) {
                   reason = JOB_CONTINUED;
                   code = 0;
           } else if (WIFSTOPPED(status)) {
                   reason = JOB_STOPPED;
                   code = WSTOPSIG(status);
           } else {
                   /* can't happen, set things to be bad. */
                   reason = UNKNOWN;
                   code = status;
           }
           finish_job(job, reason, code);
   }
   
   
   static void
   finish_job(Job *job, int reason, int code)
   {
         bool     done;          bool     done;
   
         if ((WIFEXITED(status) &&          if ((reason == JOB_EXITED &&
              WEXITSTATUS(status) != 0 && !(job->node->type & OP_IGNORE)) ||               code != 0 && !(job->node->type & OP_IGNORE)) ||
             (WIFSIGNALED(status) && WTERMSIG(status) != SIGCONT)) {              (reason == JOB_SIGNALED && code != SIGCONT)) {
                 /*                  /*
                  * If it exited non-zero and either we're doing things our                   * If it exited non-zero and either we're doing things our
                  * way or we're not ignoring errors, the job is finished.                   * way or we're not ignoring errors, the job is finished.
Line 515 
Line 556 
                  * cases, finish out the job's output before printing the exit                   * cases, finish out the job's output before printing the exit
                  * status...                   * status...
                  */                   */
                 JobClose(job);                  close_job_pipes(job);
                 done = true;                  done = true;
         } else if (WIFEXITED(status)) {          } else if (reason == JOB_EXITED) {
                 /*                  /*
                  * Deal with ignored errors in -B mode. We need to print a                   * Deal with ignored errors in -B mode. We need to print a
                  * message telling of the ignored error as well as setting                   * message telling of the ignored error as well as setting
Line 525 
Line 566 
                  * this, we set done to be true if in -B mode and the job                   * this, we set done to be true if in -B mode and the job
                  * exited non-zero.                   * exited non-zero.
                  */                   */
                 done = WEXITSTATUS(status) != 0;                  done = code != 0;
                 /*                  /*
                  * Old comment said: "Note we don't want to close down any of                   * Old comment said: "Note we don't want to close down any of
                  * the streams until we know we're at the end." But we do.                   * the streams until we know we're at the end." But we do.
                  * Otherwise when are we going to print the rest of the stuff?                   * Otherwise when are we going to print the rest of the stuff?
                  */                   */
                 JobClose(job);                  close_job_pipes(job);
         } else {          } else {
                 /*                  /*
                  * No need to close things down or anything.                   * No need to close things down or anything.
Line 539 
Line 580 
                 done = false;                  done = false;
         }          }
   
         if (done ||          if (reason == JOB_STOPPED) {
             WIFSTOPPED(status) ||                  debug_printf("Process %ld stopped.\n", (long)job->pid);
             (WIFSIGNALED(status) && WTERMSIG(status) == SIGCONT) ||                  banner(job, stdout);
             DEBUG(JOB)) {                  (void)fprintf(stdout, "*** Stopped -- signal %d\n",
                 if (WIFEXITED(status)) {                      code);
                   job->flags |= JOB_RESUME;
                   Lst_AtEnd(&stoppedJobs, job);
                   (void)fflush(stdout);
                   return;
           }
           if (reason == JOB_SIGNALED && code == SIGCONT) {
                   /*
                    * If the beastie has continued, shift the Job from the
                    * stopped list to the running one (or re-stop it if
                    * concurrency is exceeded) and go and get another
                    * child.
                    */
                   if (job->flags & (JOB_RESUME|JOB_RESTART)) {
                           banner(job, stdout);
                           (void)fprintf(stdout, "*** Continued\n");
                   }
                   if (!(job->flags & JOB_CONTINUING)) {
                           debug_printf(
                               "Warning: "
                               "process %ld was not continuing.\n",
                               (long)job->pid);
   #if 0
                           /*
                            * We don't really want to restart a job from
                            * scratch just because it continued,
                            * especially not without killing the
                            * continuing process!  That's why this is
                            * ifdef'ed out.  FD - 9/17/90
                            */
                           JobRestart(job);
   #endif
                   }
                   job->flags &= ~JOB_CONTINUING;
                   Lst_AtEnd(&runningJobs, job);
                   nJobs++;
                   debug_printf("Process %ld is continuing locally.\n",
                       (long)job->pid);
                   if (nJobs == maxJobs) {
                           jobFull = true;
                           debug_printf("Job queue is full.\n");
                   }
                   (void)fflush(stdout);
                   return;
           }
   
           if (done || DEBUG(JOB)) {
                   if (reason == JOB_EXITED) {
                         debug_printf("Process %ld exited.\n", (long)job->pid);                          debug_printf("Process %ld exited.\n", (long)job->pid);
                         if (WEXITSTATUS(status) != 0) {                          if (code != 0) {
                                 banner(job, stdout);                                  banner(job, stdout);
                                 (void)fprintf(stdout, "*** Error code %d %s\n",                                  (void)fprintf(stdout, "*** Error code %d %s\n",
                                     WEXITSTATUS(status),                                      code,
                                     (job->node->type & OP_IGNORE) ?                                      (job->node->type & OP_IGNORE) ?
                                     "(ignored)" : "");                                      "(ignored)" : "");
   
                                 if (job->node->type & OP_IGNORE) {                                  if (job->node->type & OP_IGNORE) {
                                         status = 0;                                          reason = JOB_EXITED;
                                           code = 0;
                                 }                                  }
                         } else if (DEBUG(JOB)) {                          } else if (DEBUG(JOB)) {
                                 banner(job, stdout);                                  banner(job, stdout);
                                 (void)fprintf(stdout,                                  (void)fprintf(stdout,
                                     "*** Completed successfully\n");                                      "*** Completed successfully\n");
                         }                          }
                 } else if (WIFSTOPPED(status)) {  
                         debug_printf("Process %ld stopped.\n", (long)job->pid);  
                         banner(job, stdout);  
                         (void)fprintf(stdout, "*** Stopped -- signal %d\n",  
                             WSTOPSIG(status));  
                         job->flags |= JOB_RESUME;  
                         Lst_AtEnd(&stoppedJobs, job);  
                         (void)fflush(stdout);  
                         return;  
                 } else if (WTERMSIG(status) == SIGCONT) {  
                         /*  
                          * If the beastie has continued, shift the Job from the  
                          * stopped list to the running one (or re-stop it if  
                          * concurrency is exceeded) and go and get another  
                          * child.  
                          */  
                         if (job->flags & (JOB_RESUME|JOB_RESTART)) {  
                                 banner(job, stdout);  
                                 (void)fprintf(stdout, "*** Continued\n");  
                         }  
                         if (!(job->flags & JOB_CONTINUING)) {  
                                 debug_printf(  
                                     "Warning: "  
                                     "process %ld was not continuing.\n",  
                                     (long)job->pid);  
 #if 0  
                                 /*  
                                  * We don't really want to restart a job from  
                                  * scratch just because it continued,  
                                  * especially not without killing the  
                                  * continuing process!  That's why this is  
                                  * ifdef'ed out.  FD - 9/17/90  
                                  */  
                                 JobRestart(job);  
 #endif  
                         }  
                         job->flags &= ~JOB_CONTINUING;  
                         Lst_AtEnd(&runningJobs, job);  
                         nJobs++;  
                         debug_printf("Process %ld is continuing locally.\n",  
                             (long)job->pid);  
                         if (nJobs == maxJobs) {  
                                 jobFull = true;  
                                 debug_printf("Job queue is full.\n");  
                         }  
                         (void)fflush(stdout);  
                         return;  
                 } else {                  } else {
                         banner(job, stdout);                          banner(job, stdout);
                         (void)fprintf(stdout, "*** Signal %d\n",                          (void)fprintf(stdout, "*** Signal %d\n", code);
                             WTERMSIG(status));  
                 }                  }
   
                 (void)fflush(stdout);                  (void)fflush(stdout);
Line 621 
Line 662 
         if (done &&          if (done &&
             aborting != ABORT_ERROR &&              aborting != ABORT_ERROR &&
             aborting != ABORT_INTERRUPT &&              aborting != ABORT_INTERRUPT &&
             status == 0) {              reason == JOB_EXITED && code == 0) {
                 /* As long as we aren't aborting and the job didn't return a                  /* As long as we aren't aborting and the job didn't return a
                  * non-zero status that we shouldn't ignore, we call                   * non-zero status that we shouldn't ignore, we call
                  * Make_Update to update the parents. */                   * Make_Update to update the parents. */
                 job->node->built_status = MADE;                  job->node->built_status = MADE;
                 Make_Update(job->node);                  Make_Update(job->node);
                 free(job);                  free(job);
         } else if (status != 0) {          } else if (!(reason == JOB_EXITED && code == 0)) {
                 register_error(status, job);                  register_error(reason, code, job);
                 free(job);                  free(job);
         }          }
   
Line 665 
Line 706 
         p->fd = fd[0];          p->fd = fd[0];
         close(fd[1]);          close(fd[1]);
   
         if (outputsp == NULL || p->fd > outputsn) {          if (output_mask == NULL || p->fd > largest_fd) {
                 int fdn, ofdn;                  int fdn, ofdn;
                 fd_set *tmp;  
   
                 fdn = howmany(p->fd+1, NFDBITS);                  fdn = howmany(p->fd+1, NFDBITS);
                 ofdn = outputsn ? howmany(outputsn+1, NFDBITS) : 0;                  ofdn = howmany(largest_fd+1, NFDBITS);
   
                 if (fdn != ofdn) {                  if (fdn != ofdn) {
                         tmp = recalloc(outputsp, fdn, sizeof(fd_mask));                          output_mask = erecalloc(output_mask, fdn,
                         if (tmp == NULL)                              sizeof(fd_mask));
                                 return;                          actual_mask = erecalloc(actual_mask, fdn,
                         outputsp = tmp;                              sizeof(fd_mask));
                           mask_size = fdn * sizeof(fd_mask);
                 }                  }
                 outputsn = p->fd;                  largest_fd = p->fd;
         }          }
         fcntl(p->fd, F_SETFL, O_NONBLOCK);          fcntl(p->fd, F_SETFL, O_NONBLOCK);
         FD_SET(p->fd, outputsp);          FD_SET(p->fd, output_mask);
 }  }
   
 /*-  /*-
Line 767 
Line 808 
                                 usleep(random() % random_delay);                                  usleep(random() % random_delay);
   
                 /* most cases won't return, but will exit directly */                  /* most cases won't return, but will exit directly */
                 result = run_gnode(job->node, 1);                  result = run_prepared_gnode(job->node, 1);
                 switch(result) {                  switch(result) {
                 case MADE:                  case MADE:
                         exit(0);                          exit(0);
Line 854 
Line 895 
                          * (or maxJobs is 0), it's ok to resume the job.                           * (or maxJobs is 0), it's ok to resume the job.
                          */                           */
                         bool error;                          bool error;
                         int status = 0;  
   
                         error = KILL(job->pid, SIGCONT) != 0;                          error = KILL(job->pid, SIGCONT) != 0;
   
Line 865 
Line 905 
                                  * table.                                   * table.
                                  */                                   */
                                 job->flags |= JOB_CONTINUING;                                  job->flags |= JOB_CONTINUING;
                                 W_SETTERMSIG(&status, SIGCONT);                                  finish_job(job, JOB_SIGNALED, SIGCONT);
                                 JobFinish(job, status);  
   
                                 job->flags &= ~(JOB_RESUME|JOB_CONTINUING);                                  job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
                                 debug_printf("done\n");                                  debug_printf("done\n");
                         } else {                          } else {
                                 Error("couldn't resume %s: %s",                                  Error("couldn't resume %s: %s",
                                     job->node->name, strerror(errno));                                      job->node->name, strerror(errno));
                                 W_SETEXITSTATUS(&status, 1);                                  finish_job(job, JOB_EXITED, 1);
                                 JobFinish(job, status);  
                         }                          }
                 } else {                  } else {
                         /*                          /*
Line 915 
Line 953 
          * to migrate to the node           * to migrate to the node
          */           */
         cmdsOK = Job_CheckCommands(gn, Error);          cmdsOK = Job_CheckCommands(gn, Error);
           expand_commands(gn);
   
         if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {          if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
                 /*                  /*
Line 1146 
Line 1185 
         /*          /*
          * Don't even bother if we know there's no one around.           * Don't even bother if we know there's no one around.
          */           */
         if (nJobs == 0) {          if (nJobs == 0)
                 return;                  return;
         }  
   
         while ((pid = waitpid((pid_t) -1, &status, WNOHANG|WUNTRACED)) > 0) {          while ((pid = waitpid((pid_t) -1, &status, WNOHANG|WUNTRACED)) > 0) {
                 handle_all_signals();                  handle_all_signals();
Line 1183 
Line 1221 
         }          }
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  
  * Job_CatchOutput --  
  *      Catch the output from our children.  
  * -----------------------------------------------------------------------  
  */  
 void  void
 Job_CatchOutput(void)  handle_all_jobs_output(void)
 {  {
         int nfds;          int nfds;
         struct timeval timeout;          struct timeval timeout;
Line 1199 
Line 1231 
         int i;          int i;
         int status;          int status;
   
         int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask);          /* no jobs */
         fd_set *readfdsp = malloc(count);          if (Lst_IsEmpty(&runningJobs))
                   return;
   
         (void)fflush(stdout);          (void)fflush(stdout);
         if (readfdsp == NULL)  
                 return;  
   
         memcpy(readfdsp, outputsp, count);          memcpy(actual_mask, output_mask, mask_size);
         timeout.tv_sec = SEL_SEC;          timeout.tv_sec = SEL_SEC;
         timeout.tv_usec = SEL_USEC;          timeout.tv_usec = SEL_USEC;
   
         nfds = select(outputsn+1, readfdsp, NULL, NULL, &timeout);          nfds = select(largest_fd+1, actual_mask, NULL, NULL, &timeout);
         handle_all_signals();          handle_all_signals();
         for (ln = Lst_First(&runningJobs); nfds && ln != NULL;          for (ln = Lst_First(&runningJobs); nfds && ln != NULL;
             ln = ln2) {              ln = ln2) {
Line 1218 
Line 1249 
                 job = (Job *)Lst_Datum(ln);                  job = (Job *)Lst_Datum(ln);
                 job->flags &= ~JOB_DIDOUTPUT;                  job->flags &= ~JOB_DIDOUTPUT;
                 for (i = 1; i >= 0; i--) {                  for (i = 1; i >= 0; i--) {
                         if (FD_ISSET(job->in[i].fd, readfdsp)) {                          if (FD_ISSET(job->in[i].fd, actual_mask)) {
                                 nfds--;                                  nfds--;
                                 handle_job_output(job, i, false);                                  handle_job_output(job, i, false);
                         }                          }
Line 1235 
Line 1266 
                         }                          }
                 }                  }
         }          }
         free(readfdsp);  
 }  }
   
 void  void
 handle_running_jobs()  handle_running_jobs()
 {  {
         Job_CatchOutput();          handle_all_jobs_output();
         Job_CatchChildren();          Job_CatchChildren();
 }  }
   

Legend:
Removed from v.1.111  
changed lines
  Added in v.1.112