entry widget broken in 3.99.1?

From: Grant Edwards <grante_at_reddwarf.frco.com>
Date: Mon, 18 May 1998 15:32:17 -0500

I've found a bug in process.c that prevents you from getting the exit status
of a child process under certain circumstances.

Description of problem:

In the handler for SIGCHLD, a call is made to waitpid() to check the status
of each of the child processes. In the current version of process.c, a
non-zero return value is interpreted as meaning that the child has exited
and the child's status has been read:

static int internal_process_alivep(SCM process)

...
    res = waitpid(PROCPID(process), &info, WNOHANG);
    if (res == 0)
      /* process is still running */
      return TRUE;
    else {
      /* process has terminated and we must save this information */
      PROCESS(process)->exited = TRUE;
      PROCESS(process)->exit_status = info;
      return FALSE;
...


This is not always the case. If (process-wait [process]) has been called to
wait on a particular child, it will read the status, and the waitpid() call
in the SIGCHLD handler will return a -1 (no such child). The info field
doesn't contain any data and it is written over the exit_status that was
saved by (process-wait [process]) -- thus the exit_status field now contains
random garbage.

The fix:

Always check the return status from waitpid() to see if it returns the
requested pid:

    res = waitpid(PROCPID(process), &info, WNOHANG);
    if (res == 0)
      {
      /* process is still running */
      return TRUE;
      }
    else if (res == PROCPID(process))
      {
      /* process has terminated and we must save this information */
      PROCESS(process)->exited = TRUE;
      PROCESS(process)->exit_status = info;
      return FALSE;
      }
    else
      {
      /* might not have found process because we've already waited for it */
      /* if so, then status has already been updated */
      return FALSE;
      }

[All calls to waitpid() should be handled similarly]


I've included a context diff below. It has only been tested on Linux
2.0.31, but judging by the man pages, it ought to work for Solaris 2.5 also.

Unfortunately, I re-indented bsome of the code while I was debugging, and
haven't un-done my formatting changes. So, there are cosmetic-only changes
reflected in some of the patch. Sorry.

------------------------------cut here------------------------------
*** process.c.old Fri Apr 10 02:13:18 1998
--- process.c Thu May 14 09:05:51 1998
***************
*** 81,108 ****
  }
  
  static int internal_process_alivep(SCM process)
! {
! int info, res;
  
    if (PROCESS(process)->exited)
      return FALSE;
! else {
      /* Use waitpid to gain the info. */
      res = waitpid(PROCPID(process), &info, WNOHANG);
      if (res == 0)
        /* process is still running */
        return TRUE;
! else {
        /* process has terminated and we must save this information */
        PROCESS(process)->exited = TRUE;
        PROCESS(process)->exit_status = info;
        return FALSE;
      }
    }
! }
  
  static void process_terminate_handler(int sig) /* called when a child dies */
! {
    register int i;
    SCM proc;
  
--- 81,118 ----
  }
  
  static int internal_process_alivep(SCM process)
! {
! int info = 0, res = -1;
    
    if (PROCESS(process)->exited)
      return FALSE;
! else
! {
      /* Use waitpid to gain the info. */
      res = waitpid(PROCPID(process), &info, WNOHANG);
      if (res == 0)
+ {
        /* process is still running */
        return TRUE;
! }
! else if (res == PROCPID(process))
! {
        /* process has terminated and we must save this information */
        PROCESS(process)->exited = TRUE;
        PROCESS(process)->exit_status = info;
        return FALSE;
        }
+ else
+ {
+ /* might not have found process because we've already waited for it */
+ /* if so, then status has already been updated */
+ return FALSE;
        }
! }
! }
  
  static void process_terminate_handler(int sig) /* called when a child dies */
! {
    register int i;
    SCM proc;
  
***************
*** 110,118 ****
    static int in_handler = 0;
  
    signal(SIGCHLD, process_terminate_handler); /* Necessary on System V */
! if (in_handler++) /* Execution is re-entrant */ return;
    
! do {
  #endif
      /* Find the process which is terminated
       * Note that this loop can find:
--- 120,130 ----
    static int in_handler = 0;
  
    signal(SIGCHLD, process_terminate_handler); /* Necessary on System V */
! if (in_handler++) /* Execution is re-entrant */
! return;
    
! do
! {
  #endif
      /* Find the process which is terminated
       * Note that this loop can find:
***************
*** 124,130 ****
       * PURGE_PROCESS_TABLE
       * Sometimes I think that life is a little bit complicated....
       */
! for(i = 0; i<MAX_PROC_NUM; i++) {
        proc = proc_arr[i];
        if (PROCESSP(proc) && !internal_process_alivep(proc))
          /* This process has exited. We can delete it from the table */
--- 136,143 ----
       * PURGE_PROCESS_TABLE
       * Sometimes I think that life is a little bit complicated....
       */
! for(i = 0; i<MAX_PROC_NUM; i++)
! {
        proc = proc_arr[i];
        if (PROCESSP(proc) && !internal_process_alivep(proc))
          /* This process has exited. We can delete it from the table */
***************
*** 138,144 ****
       */
    } while (--in_handler > 0);
  #endif
! }
  
  
  static SCM make_process(void)
--- 151,157 ----
       */
      } while (--in_handler > 0);
  #endif
! }
  
  
  static SCM make_process(void)
***************
*** 446,489 ****
  }
  
  static PRIMITIVE process_wait(SCM process)
! {
    PURGE_PROCESS_TABLE();
  
    if(NPROCESSP(process)) Err("process-wait: bad process", process);
    
    if (PROCESS(process)->exited) return Ntruth;
! else {
! int ret = waitpid(PROCPID(process), &PROCESS(process)->exit_status, 0);
  
      PROCESS(process)->exited = TRUE;
! return (ret == 0) ? Ntruth : Truth;
    }
! }
  
  
  static PRIMITIVE process_xstatus(SCM process)
! {
! int info, n;
  
    PURGE_PROCESS_TABLE();
  
! if (NPROCESSP(process)) Err("process-exit-status: bad process", process);
    
! if (PROCESS(process)->exited) n = PROCESS(process)->exit_status;
! else {
! if (waitpid(PROCPID(process), &info, WNOHANG) == 0) {
        /* Process is still running */
        return Ntruth;
      }
! else {
        /* Process is now terminated */
        PROCESS(process)->exited = TRUE;
        PROCESS(process)->exit_status = info;
        n = WEXITSTATUS(info);
      }
    }
    return STk_makeinteger((long) n);
! }
  
  static PRIMITIVE process_send_signal(SCM process, SCM signal)
  {
--- 459,519 ----
  }
  
  static PRIMITIVE process_wait(SCM process)
! {
    PURGE_PROCESS_TABLE();
  
    if(NPROCESSP(process)) Err("process-wait: bad process", process);
    
    if (PROCESS(process)->exited) return Ntruth;
! else
! {
! int info=0, res=-1;
  
+ res = waitpid(PROCPID(process), &info, 0);
+
+ if (res == PROCPID(process))
+ {
+ PROCESS(process)->exit_status = info;
        PROCESS(process)->exited = TRUE;
! return Truth;
        }
! else
! return Ntruth;
! }
! }
  
  
  static PRIMITIVE process_xstatus(SCM process)
! {
! int info, n, res;
  
    PURGE_PROCESS_TABLE();
  
! if (NPROCESSP(process))
! Err("process-exit-status: bad process", process);
    
! if (PROCESS(process)->exited)
! n = WEXITSTATUS(PROCESS(process)->exit_status);
! else
! {
! res = waitpid(PROCPID(process), &info, WNOHANG);
! if (res == 0)
! {
        /* Process is still running */
        return Ntruth;
        }
! else if (res == PROCPID(process))
! {
        /* Process is now terminated */
        PROCESS(process)->exited = TRUE;
        PROCESS(process)->exit_status = info;
        n = WEXITSTATUS(info);
        }
+ else
+ return Ntruth;
      }
    return STk_makeinteger((long) n);
! }
  
  static PRIMITIVE process_send_signal(SCM process, SCM signal)
  {
------------------------------cut here------------------------------
Received on Mon May 18 1998 - 21:54:02 CEST

This archive was generated by hypermail 2.3.0 : Mon Jul 21 2014 - 19:38:59 CEST