From xemacs-m  Sat Jan 25 06:44:46 1997
Received: from portofix.ida.liu.se (portofix.ida.liu.se [130.236.177.25])
          by xemacs.org (8.8.4/8.8.4) with ESMTP
	  id GAA27182 for <xemacs-beta@xemacs.org>; Sat, 25 Jan 1997 06:44:45 -0600 (CST)
Received: from sen2.ida.liu.se (sen2.ida.liu.se [130.236.176.112]) by portofix.ida.liu.se (8.8.3/8.8.3) with SMTP id NAA01210 for <xemacs-beta@xemacs.org>; Sat, 25 Jan 1997 13:44:46 +0100 (MET)
Received: by sen2.ida.liu.se (SMI-8.6/ida.slave-V1.0b6d6S2)
	id NAA09554; Sat, 25 Jan 1997 13:44:45 +0100
Date: Sat, 25 Jan 1997 13:44:45 +0100
Message-Id: <199701251244.NAA09554@sen2.ida.liu.se>
From: David Byers <davby@ida.liu.se>
To: xemacs-beta@xemacs.org
Subject: Re: Sort of a bug in Fsit-for and Fsleep-for
In-Reply-To: <rv3evsjic3.fsf@sdnp5.ucsd.edu>
References: <199701221635.RAA00914@sen2.ida.liu.se>
	<kigohego1eh.fsf@jagor.srce.hr>
	<QQbztk06098.199701231702@crystal.WonderWorks.COM>
	<rv3evsjic3.fsf@sdnp5.ucsd.edu>
X-Face: (@~#v$c[GP"T}a;|MU<%Dpm5*6yv"NR|7k;uk8MAISFxdZ(Og$C{u(j"9X7v$qonp}SKfhT
 g|5[Pu~/3F7XQEk70gK'4z%1R%%gg7]}=>/jD`qcBeHDgo&HS,^S!&.zoTSxh<>-O6EB?SSy96&m37

> [From: David Moore <dmoore@UCSD.EDU>]
> 	I noticed the potential for this bad recursive behaviour when I
> fixed the previous sit-for bug (that fix didn't change these
> semantics).  I was going to try to solve this problem, but it's not
> totally clean, and I hoped it wasn't biting anyone.

Well, it sure bit me.

Firstly, I think I can fix sit-for and sleep-for reasonably cleanly. By
adding a queue similar to command_event_queue for process events,
Fsit_for and Fsleep_for can defer processing of these events to after
they finish, as is done with command events. This appears to work well.

Secondly, I took a long hard look at accept-process-output and noticed
that the semantics of accept-process-output in XEmacs are significantly
different from Gnu Emacs when the PROCESS argument is nil. In Gnu Emacs
accept-process-output will wait for output from *any* process for the
specified amount of time. In XEmacs accept-process-output terminates
immediately. Since I write elisp that reads data from many different
processes and loops around accept-process-output to block until
everything has arrived, this is a Very Bad Thing.

I also checked for recursive invocation of filter functions from
accept-process-output, and found that they happen in both Gnu Emacs and
XEmacs. I can easily imageine code relying on this (in face, I've even
written code that does), so I didn't do anything about it.


I've included the patch for event-stream.c below. The Lisp manual should
also be changed to document the semantics of accept-process-output and
to note that calling accept-process-output from a filter function may
result in the filter function being called recursively. I've tested the
patches by running a program I use to check semantics of sit-for,
sleep-for and accept-process-output, and by running a conferencing
system front-end (heavy on accept-process-output and caller of sit-for
in process filters), and so far I haven't discovered any problems.

I would really like to know whether or not fixes for sit-for, sleep-for
and accept-process-output along these lines are included in 19.15 since
that has a direct impact on what my Lisp code needs to look like.

--
David Byers.



*** event-stream.c	Fri Jan 24 13:35:14 1997
--- event-stream-new.c	Fri Jan 24 16:23:46 1997
*************** static void maybe_kbd_translate (Lisp_Ob
*** 251,256 ****
--- 251,258 ----
     Chained through event_next()
     command_event_queue_tail is a pointer to the last-added element.
   */
+ static Lisp_Object process_event_queue;
+ static Lisp_Object process_event_queue_tail;
  static Lisp_Object command_event_queue;
  static Lisp_Object command_event_queue_tail;
  
*************** It will not work to call this function o
*** 1446,1451 ****
--- 1448,1466 ----
  /*                    enqueuing and dequeuing events                  */
  /**********************************************************************/
  
+ /* Add an event to the back of the process_event_queue */
+ void
+ enqueue_process_event (Lisp_Object event)
+ {
+   enqueue_event (event, &process_event_queue, &process_event_queue_tail);
+ }
+ 
+ Lisp_Object
+ dequeue_process_event (void)
+ {
+   return dequeue_event (&process_event_queue, &process_event_queue_tail);
+ }
+ 
  /* Add an event to the back of the command-event queue: it will be the next
     event read after all pending events.   This only works on keyboard,
     mouse-click, misc-user, and eval events.
*************** in_single_console_state (void)
*** 1837,1843 ****
  Charcount num_input_chars;
  
  static void
! next_event_internal (Lisp_Object target_event, int allow_queued)
  {
    struct gcpro gcpro1;
    /* QUIT;   This is incorrect - the caller must do this because some
--- 1852,1859 ----
  Charcount num_input_chars;
  
  static void
! next_event_internal (Lisp_Object target_event, int allow_queued,
!                      int allow_deferred)
  {
    struct gcpro gcpro1;
    /* QUIT;   This is incorrect - the caller must do this because some
*************** next_event_internal (Lisp_Object target_
*** 1863,1868 ****
--- 1879,1899 ----
  	}
  #endif
      }
+   else if (allow_deferred && !NILP (process_event_queue))
+     {
+       Lisp_Object event = dequeue_process_event ();
+       Fcopy_event (event, target_event);
+       Fdeallocate_event (event);
+ #ifdef DEBUG_EMACS
+       if (debug_emacs_events)
+ 	{
+ 	  write_c_string ("(process event queue) ",
+ 			  Qexternal_debugging_output);
+ 	  print_internal (target_event, Qexternal_debugging_output, 1);
+ 	  write_c_string ("\n", Qexternal_debugging_output);
+ 	}
+ #endif
+     }
    else
      {
        struct Lisp_Event *e = XEVENT (target_event);
*************** The returned event will be one of the fo
*** 2100,2106 ****
  	{
  	  run_pre_idle_hook ();
  	  redisplay ();
! 	  next_event_internal (event, 1);
  	  Vquit_flag = Qnil; /* Read C-g as an event. */
  	  store_this_key = 1;
  	}
--- 2131,2137 ----
  	{
  	  run_pre_idle_hook ();
  	  redisplay ();
! 	  next_event_internal (event, 1, 1);
  	  Vquit_flag = Qnil; /* Read C-g as an event. */
  	  store_this_key = 1;
  	}
*************** A user event is a key press, button pres
*** 2303,2309 ****
        /* This will take stuff off the command_event_queue, or read it
  	 from the event_stream, but it will not block.
         */
!       next_event_internal (event, 1);
        Vquit_flag = Qnil; /* Treat C-g as a user event (ignore it).
  			    It is vitally important that we reset
  			    Vquit_flag here.  Otherwise, if we're
--- 2334,2340 ----
        /* This will take stuff off the command_event_queue, or read it
  	 from the event_stream, but it will not block.
         */
!       next_event_internal (event, 1, 1);
        Vquit_flag = Qnil; /* Treat C-g as a user event (ignore it).
  			    It is vitally important that we reset
  			    Vquit_flag here.  Otherwise, if we're
*************** DEFUN ("accept-process-output", Faccept_
*** 2365,2371 ****
  Allow any pending output from subprocesses to be read by Emacs.
  It is read into the process' buffers or given to their filter functions.
  Non-nil arg PROCESS means do not return until some output has been received
!  from PROCESS.
  If the second arg is non-nil, it is the maximum number of seconds to wait:
   this function will return after that much time even if no input has arrived
   from PROCESS.  This argument may be a float, meaning wait some fractional
--- 2396,2403 ----
  Allow any pending output from subprocesses to be read by Emacs.
  It is read into the process' buffers or given to their filter functions.
  Non-nil arg PROCESS means do not return until some output has been received
!  from PROCESS. Nil arg PROCESS means do not return until some output has
!  been received from any process.
  If the second arg is non-nil, it is the maximum number of seconds to wait:
   this function will return after that much time even if no input has arrived
   from PROCESS.  This argument may be a float, meaning wait some fractional
*************** Return non-nil iff we received any outpu
*** 2383,2388 ****
--- 2415,2421 ----
    Lisp_Object result = Qnil;
    int timeout_id;
    int timeout_enabled = 0;
+   int done = 0;
    struct buffer *old_buffer = current_buffer;
  
    /* We preserve the current buffer but nothing else.  If a focus
*************** Return non-nil iff we received any outpu
*** 2395,2401 ****
  
    GCPRO2 (event, process);
  
!   if (!NILP (process) && (!NILP (timeout_secs) || !NILP (timeout_msecs)))
      {
        unsigned long msecs = 0;
        if (!NILP (timeout_secs))
--- 2428,2434 ----
  
    GCPRO2 (event, process);
  
!   if (!NILP (timeout_secs) || !NILP (timeout_msecs))
      {
        unsigned long msecs = 0;
        if (!NILP (timeout_secs))
*************** Return non-nil iff we received any outpu
*** 2414,2420 ****
  
    event = Fmake_event ();
  
!   while (!NILP (process)
  	 /* Calling detect_input_pending() is the wrong thing here, because
  	    that considers the Vunread_command_events and command_event_queue.
  	    We don't need to look at the command_event_queue because we are
--- 2447,2453 ----
  
    event = Fmake_event ();
  
!   while (!done
  	 /* Calling detect_input_pending() is the wrong thing here, because
  	    that considers the Vunread_command_events and command_event_queue.
  	    We don't need to look at the command_event_queue because we are
*************** Return non-nil iff we received any outpu
*** 2435,2441 ****
        if (!event_stream_wakeup_pending_p (timeout_id, 0))
  	{
  	  timeout_enabled = 0;
! 	  process = Qnil;	/* We're  done. */
  	}
  
        QUIT;	/* next_event_internal() does not QUIT, so check for ^G
--- 2468,2474 ----
        if (!event_stream_wakeup_pending_p (timeout_id, 0))
  	{
  	  timeout_enabled = 0;
!         done = 1;             /* We're  done. */
  	}
  
        QUIT;	/* next_event_internal() does not QUIT, so check for ^G
*************** Return non-nil iff we received any outpu
*** 2443,2449 ****
  		   less likely that the filter will actually be aborted.
  		 */
  
!       next_event_internal (event, 0);
        /* If C-g was pressed while we were waiting, Vquit_flag got
  	 set and next_event_internal() also returns C-g.  When
  	 we enqueue the C-g below, it will get discarded.  The
--- 2476,2482 ----
  		   less likely that the filter will actually be aborted.
  		 */
  
!       next_event_internal (event, 0, 1);
        /* If C-g was pressed while we were waiting, Vquit_flag got
  	 set and next_event_internal() also returns C-g.  When
  	 we enqueue the C-g below, it will get discarded.  The
*************** Return non-nil iff we received any outpu
*** 2452,2460 ****
  	{
  	case process_event:
  	  {
! 	    if (EQ (XEVENT (event)->event.process.process, process))
  	      {
! 		process = Qnil;
  		/* RMS's version always returns nil when proc is nil,
  		   and only returns t if input ever arrived on proc. */
  		result = Qt;
--- 2485,2494 ----
  	{
  	case process_event:
  	  {
! 	    if (NILP (process) ||
!                 EQ (XEVENT (event)->event.process.process, process))
  	      {
!               done = 1;
  		/* RMS's version always returns nil when proc is nil,
  		   and only returns t if input ever arrived on proc. */
  		result = Qt;
*************** ARG may be a float, meaning pause for so
*** 2521,2535 ****
  	 consumer as well.  We don't care about command and eval-events
  	 anyway.
         */
!       next_event_internal (event, 0); /* blocks */
        /* See the comment in accept-process-output about Vquit_flag */
        switch (XEVENT_TYPE (event))
  	{
  	case timeout_event:
  	  /* We execute the event even if it's ours, and notice that it's
  	     happened above. */
  	case pointer_motion_event:
- 	case process_event:
  	case magic_event:
            {
            EXECUTE_INTERNAL:
--- 2555,2576 ----
  	 consumer as well.  We don't care about command and eval-events
  	 anyway.
         */
!       next_event_internal (event, 0, 0); /* blocks */
        /* See the comment in accept-process-output about Vquit_flag */
        switch (XEVENT_TYPE (event))
  	{
+         case process_event:
+           {
+             /* Avoid calling filter functions recursively by squirreling
+                away process events */
+             enqueue_process_event (Fcopy_event (event, Qnil));
+             goto DONE_LABEL;
+           }
+ 
  	case timeout_event:
  	  /* We execute the event even if it's ours, and notice that it's
  	     happened above. */
  	case pointer_motion_event:
  	case magic_event:
            {
            EXECUTE_INTERNAL:
*************** Value is t if waited the full time with 
*** 2627,2633 ****
  	 consumer as well.  In fact, we know there's nothing on the
  	 command_event_queue that we didn't just put there.
         */
!       next_event_internal (event, 0); /* blocks */
        /* See the comment in accept-process-output about Vquit_flag */
  
        if (command_event_p (event))
--- 2668,2674 ----
  	 consumer as well.  In fact, we know there's nothing on the
  	 command_event_queue that we didn't just put there.
         */
!       next_event_internal (event, 0, 0); /* blocks */
        /* See the comment in accept-process-output about Vquit_flag */
  
        if (command_event_p (event))
*************** Value is t if waited the full time with 
*** 2643,2648 ****
--- 2684,2697 ----
  	    enqueue_command_event (Fcopy_event (event, Qnil));
  	    break;
  	  }
+ 
+         case process_event:
+           {
+             /* Avoid recursive calls to process filters */
+             enqueue_process_event (Fcopy_event (event, Qnil));
+             break;
+           }
+           
  	case timeout_event:
  	  /* We execute the event even if it's ours, and notice that it's
  	     happened above. */
*************** wait_delaying_user_input (int (*predicat
*** 2697,2703 ****
  	 command_event_queue; there are only user and eval-events there,
  	 and we'd just have to put them back anyway.
         */
!       next_event_internal (event, 0);
        /* See the comment in accept-process-output about Vquit_flag */
        if (command_event_p (event)
            || (XEVENT_TYPE (event) == eval_event)
--- 2746,2752 ----
  	 command_event_queue; there are only user and eval-events there,
  	 and we'd just have to put them back anyway.
         */
!       next_event_internal (event, 0, 1);
        /* See the comment in accept-process-output about Vquit_flag */
        if (command_event_p (event)
            || (XEVENT_TYPE (event) == eval_event)
*************** vars_of_event_stream (void)
*** 4122,4127 ****
--- 4171,4180 ----
    command_event_queue = Qnil;
    staticpro (&command_event_queue);
    command_event_queue_tail = Qnil;
+ 
+   process_event_queue = Qnil;
+   staticpro (&process_event_queue);
+   process_event_queue_tail = Qnil;
  
    Vlast_selected_frame = Qnil;
    staticpro (&Vlast_selected_frame);



