Subject: bug#9087: Crash reading from minibuffer with icomplete-mode

From: Eli Zaretskii <[email-masked]>
Date: Sat, 07 Jan 2012 16:59:18 +0200

> Date: Sat, 07 Jan 2012 14:57:31 +0100
> From: martin rudalics <[email-masked]>
> CC: [email-masked], [email-masked], [email-masked], 
>  [email-masked], [email-masked]
> 
>  >>  > You can verify yourself that, in Emacs without
>  >>  > the patch I sent, typing C-g when triggerbug.el says "now!" does not
>  >>  > cause a crash, but a "Quit", albeit a slightly (by 1-2 seconds on my
>  >>  > box) delayed one.
>  >>
>  >> Because `signal' resets immediate_quit to zero?
>  >
>  > You mean, as explanation of the delay?
> 
> No.  I meant as an explanation of the non-crash.
> 
>  > No, I don't think so.  I think
>  > Emacs simply doesn't see C-g until the lengthy calculation is over.
> 
> Likely.  But where is C-g treated differently from other input?

Here:

  static void
  post_character_message (HWND hwnd, UINT msg,
			  WPARAM wParam, LPARAM lParam,
			  DWORD modifiers)
  {
    W32Msg wmsg;

    wmsg.dwModifiers = modifiers;

    /* Detect quit_char and set quit-flag directly.  Note that we
       still need to post a message to ensure the main thread will be
       woken up if blocked in sys_select, but we do NOT want to post
       the quit_char message itself (because it will usually be as if
       the user had typed quit_char twice).  Instead, we post a dummy
       message that has no particular effect. */
    {
      int c = wParam;
      if (isalpha (c) && wmsg.dwModifiers == ctrl_modifier)
	c = make_ctrl_char (c) & 0377;
      if (c == quit_char
	  || (wmsg.dwModifiers == 0 &&
	      w32_quit_key && wParam == w32_quit_key))
	{
	  Vquit_flag = Qt;

	  /* The choice of message is somewhat arbitrary, as long as
	     the main thread handler just ignores it. */
	  msg = WM_NULL;

	  /* Interrupt any blocking system calls.  */
	  signal_quit ();

	  /* As a safety precaution, forcibly complete any deferred
	     messages.  This is a kludge, but I don't see any particularly
	     clean way to handle the situation where a deferred message is
	     "dropped" in the lisp thread, and will thus never be
	     completed, eg. by the user trying to activate the menubar
	     when the lisp thread is busy, and then typing C-g when the
	     menubar doesn't open promptly (with the result that the
	     menubar never responds at all because the deferred
	     WM_INITMENU message is never completed).  Another problem
	     situation is when the lisp thread calls SendMessage (to send
	     a window manager command) when a message has been deferred;
	     the lisp thread gets blocked indefinitely waiting for the
	     deferred message to be completed, which itself is waiting for
	     the lisp thread to respond.

	     Note that we don't want to block the input thread waiting for
	     a response from the lisp thread (although that would at least
	     solve the deadlock problem above), because we want to be able
	     to receive C-g to interrupt the lisp thread.  */
	  cancel_all_deferred_msgs ();
	}
      else
	signal_user_input ();
    }

    my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
  }

And signal_user_input then does:

  static void
  signal_user_input (void)
  {
    /* Interrupt any lisp that wants to be interrupted by input.  */
    if (!NILP (Vthrow_on_input))
      {
	Vquit_flag = Vthrow_on_input;
	/* If we're inside a function that wants immediate quits,
	   do it now.  */
	if (immediate_quit && NILP (Vinhibit_quit))
	  {
	    immediate_quit = 0;
	    QUIT;
	  }
      }
  }

This call to QUIT is the problem, because this code runs in a
different thread than the main Lisp thread, the one that set up the
setjmp point to which we longjmp when we throw to top level.  So it
unwinds the wrong stack.

By contrast, C-g does not call signal_user_input to be called, see
above.  So it avoids this fate.