🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: gnu.emacs.bug
Article #98941

Re: start-process bug/misfeature

#98941
From: Richard Mlynarik
Date: Fri, 11 Jan 1991 17:38
172 lines
7592 bytes
In article <MOSS.91Jan3105230@ibis.cs.umass.edu> moss@cs.umass.edu (Eliot Moss) writes:

   Well, thsi does not qualify as a bug in the sense of something not acting as
   advertised, but it is a problem that should perhaps be addressed. The Emacs
   built-in function start-process allows one to set many options, but setting
   the process filter is not one of them. This means that there is a race
   condition between starting the process and setting a filter on it. This does
   not matter much for some processes (e.g., loadst, since it will produce output
   again after a while if you wait) but it matters greatly for others (e.g.,
   invoking hostname to get the current host's name). Granted, there *are* kludgy
   workarounds, such as "sh -c cat < /dev/null; hostname", which hangs until you
   send an eof to the cat, but it's pretty distasteful and hard for someone to
   devise on their own.

   My suggestion is to be able to set the process filter as an optional
   additional argument to start-process. This should be upwardly compatible and
   simple to implement. How about it?

>From memory, I don't believe that there is a race condition because
process-output in Emacs is accepted synchronously by explicitly
polling in various Emacs functions (which include
accept-process-output, sleep-for/sit-for and read-char).  The Emacs
interpreter itself does not poll for input, so Emacs lisp code which
doesn't directly or indirectly call one of the polling functions
executes atomically with respect to process-output.

None of this means that reading code which relies on this behaviour
isn't a stomach-churning experience.  It does however mean that the
code works.

I actually wrote what I thought was better process-spawning code in
late 1986, but Stallman didn't want it.  Here's what it looked like:
(there were also changes to create_process to support the new
channel-types;  I expect that code has truly bit-rotted by now)

/*
DEFUN ("start-subprocess", Fstart_subprocess, Sstart_subprocess, 3, MANY, 0,
  "Start a program in a subprocess.  Return the process object for it.\n\
Args are NAME PROGRAM PROGRAM-ARGS &rest OPTIONS
NAME is name for process.  It is modified if necessary to make it unique.\n\
Second arg is program file name.  It is searched for as in the shell.\n\
Third arg is a list or strings to give program as arguments.\n\
OPTIONS is a list of alternating option-names and values.\n\
 Each option-name is a symbol, and must be one of the following:\n\
 `buffer' is the buffer or (buffer-name) to associate with the process.\n\
   Process output goes at end of that buffer, unless you specify\n\
   a filter function to handle the output.\n\
   By default buffer is nil, meaning that this process is not associated\n\
   with any buffer\n\
 `filter' is a function to be called each time the process does output\n\
   The filter gets two arguments: the process and the string of output.\n\
   If the process has a filter, its buffer is not used for output\n\
   (Unless the filter inserts output there itself)\n\
 `sentinel' is a function to be called when the process changes state.\n\
   It gets two arguments: the process, and a string describing the change.\n\
 `kill-without-query' means not to query the user if this process\n\
   is running when Emacs is exited.  (Just kill the process silently)\n\
 `channel-type' specifies what kind of communication channel to make\n\
   between Emacs and the subprocess.  It should be one of the following\n\
   symbols:
   `pty' -- a pseudo-terminal.\n\
   `raw-pty' -- a pseudo-terminal with line-editing disabled.\n\
   `terminal' -- a pseudo-terminal configured for interactive use.\n\
   `pipe' or `socket' -- a socket (pipe)\n\
   Note that not all un*x versions will support all of the above.\n\
   If channel-type is not specified, Emacs uses some default type.")
*/

/* (defun start-process (name buffer programe &rest program-args)
     (start-subprocess name program program-args 'buffer buffer))
*/

DEFUN ("start-subprocess", Fstart_subprocess, Sstart_subprocess, 3, MANY, 0, 0)
  (nargs, args)
     int nargs;
     Lisp_Object *args;
{
  Lisp_Object proc, tem;
  Lisp_Object name, program, buffer, filter, sentinel;
  Lisp_Object kwoq, chan_type;
  unsigned char **new_argv;
  register int i;
  Lisp_Object *ap;

  name = args[0];
  CHECK_STRING (name, 0);

  program = args[1];
  CHECK_STRING (program, 1);

  tem = args[2];
  CHECK_LIST (tem, 2);
  i = XFASTINT (Flength (tem));
  new_argv = (unsigned char **) alloca ((i + 2) * sizeof (char *));
  
  for (i = 1; !NULL (tem); tem = Fcdr (tem), i++)
    {
      register Lisp_Object arg = Fcar (tem);
      CHECK_STRING (arg, 2);
      new_argv[i] = XSTRING (arg)->data;
    }
  new_argv[i] = 0;
  new_argv[0] = XSTRING (program)->data;

  /* If program file name is not absolute, search our path for it */
  if (new_argv[0][0] != '/')
    {
      tem = Qnil;
      openp (Vexec_path, program, "", &tem, 1);
      if (NULL (tem))
	report_file_error ("Searching for program", Fcons (program, Qnil));
      new_argv[0] = XSTRING (tem)->data;
    }

  buffer = Qnil; filter = Qnil; sentinel = Qnil;
  kwoq = Qnil; chan_type = Qnil;

  if ((nargs - 3) & 1)
    error ("Odd-length options list");
  for (ap = args + 3, i = 3; i < nargs; ap++, i++)
    {
      register unsigned char *s;
      tem = *ap;
      CHECK_SYMBOL (tem, i);
      s = (XSYMBOL (tem)->name)->data;
      ap++; i++;
      if (!strcmp (s, "buffer"))
	{
	  buffer = *ap;
	  if (!NULL (buffer)) buffer = Fget_buffer_create (buffer);
	}
      else if (!strcmp (s, "filter"))
	filter = *ap;
      else if (!strcmp (s, "sentinel"))
	sentinel = *ap;
      else if (!strcmp (s, "kill-without-query"))
	kwoq = *ap;
      else if (!strcmp (s, "channel-type"))
	{
	  chan_type = *ap;
	  CHECK_SYMBOL (chan_type, 0);
	  s = (XSYMBOL (chan_type)->name)->data;
	  if (!strcmp (s, "pipe") || !strcmp (s, "socket"))
	    chan_type = make_number (PIPE_CHAN);
	  else if (!strcmp (s, "raw-pty"))
	    chan_type = make_number (RAW_PTY_CHAN);
	  else if (!strcmp (s, "pty"))
	    chan_type = make_number (PTY_CHAN);
	  else if (!strcmp (s, "terminal"))
	    chan_type = make_number (TERMINAL_PTY_CHAN);
	  else
	    error ("Unknown channel type: %s", s);
	}
      else
	error ("Unknown start-subprocess option: %s", s);
    }

  proc = make_process (name);

  XPROCESS (proc)->childp = Qt;
  XPROCESS (proc)->command_channel_p = Qnil;
  XPROCESS (proc)->buffer = buffer;
  XPROCESS (proc)->sentinel = sentinel;
  XPROCESS (proc)->filter = filter;
  XPROCESS (proc)->kill_without_query = kwoq;
  XPROCESS (proc)->command = Fcons (args[1], Fcopy_sequence (args[2]));
  XPROCESS (proc)->channel_type = NULL (chan_type) 
				  ? make_number (DONT_CARE_CHAN) : chan_type;
  create_process (proc, new_argv);

  return proc;
}
Technical Details
Message-ID: <MLY.91Jan11123825@mintaka.lcs.mit.edu>
Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!tut.cis.ohio-state.edu!MINTAKA.LCS.MIT.EDU!mly

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: fb2f1,778397e09b3f4faf
X-Google-Attributes: gidfb2f1,public
X-Google-ArrivalTime: 1991-01-11 09:38:25 PST
Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!tut.cis.ohio-state.edu!MINTAKA.LCS.MIT.EDU!mly
From: mly@MINTAKA.LCS.MIT.EDU (Richard Mlynarik)
Newsgroups: gnu.emacs.bug
Subject: Re: start-process bug/misfeature
Message-ID: <MLY.91Jan11123825@mintaka.lcs.mit.edu>
Date: 11 Jan 91 17:38:25 GMT
Sender: daemon@tut.cis.ohio-state.edu
Distribution: gnu
Organization: ITS Preservation Society
Lines: 172
Approved: bug-gnu-emacs@prep.ai.mit.edu