Subject: Re: bug#78737: sit-for behavior changes when byte-compiled

From: Pip Cet <[email-masked]>
Date: Sat, 14 Jun 2025 07:55:54 +0000

"Eli Zaretskii" <[email-masked]> writes:

>> Date: Fri, 13 Jun 2025 18:25:13 +0000
>> From: Pip Cet <[email-masked]>
>> Cc: Eli Zaretskii <[email-masked]>, [email-masked], [email-masked]
>>
>> "Stefan Monnier" <[email-masked]> writes:
>>
>> >> What is a "normal quit"?
>> >
>> > A single C-g.
>> >
>> >> What is "emergency quit"?
>> >
>> > Something like `C-g C-g C-g`, tho we don't have that implemented,
>> > currently, AFAIK.
>>
>> It's implemented.  See handle_interrupt in keyboard.c:
>>
>>       /* Request quit when it's safe.  */
>>       int count = NILP (Vquit_flag) ? 1 : force_quit_count + 1;
>>       force_quit_count = count;
>>       if (count == 3)
>> 	Vinhibit_quit = Qnil;
>>       Vquit_flag = Qt;
>
> That's not "emergency quit", that's part of "emergency exit".  A
> different, though related, feature.

Sorry, still being dense.  This code is not reached on text terminals,
so it's not part of what trouble.texi calls the Emergency Escape
mechanism.  I thought you were using "emergency exit" to refer to the
Emergency Escape feature, but the above suggests that "emergency exit"
includes both the Emergency Escape and triple-C-g behavior on GUI
terminals.

I'd like a term for just the latter.

Triple C-g on a GUI terminal clears inhibit-quit and quits, breaking out
of loops such as:

(let ((inhibit-quit t))
  (while t))

This:

1. requires *three* C-g presses
2. does not trigger an Emergency Escape or exit Emacs
3. is unreachable on text terminals

Since this behavior does not exit Emacs, I thought it would not count as
part of the "emergency exit" feature.

>> The "emergency" part is clearing the inhibit-quit flag when
>> force_quit_count reaches 3.  The bug is that it should be >= 3, and the
>> questionable design feature is that we should reset the counter if
>> Vquit_flag and Vinhibit_quit are both nil, not just based on Vquit_flag.
>
> It isn't a bug, because emergency exit happens when the count is 2.

No, nothing special happens when the count reaches 2.

These two are mutually exclusive:

The code incrementing force_quit_count is unreachable on text terminals.

The code implementing the Emergency Escape feature is unreachable on GUI
terminals.

static void
handle_interrupt (bool in_signal_handler)
{
  char c;

  cancel_echoing ();

  /* XXX This code needs to be revised for multi-tty support.  */
  if (!NILP (Vquit_flag) && get_named_terminal (dev_tty))
    {
      <Emergency Escape feature>
    }
  else
    {
      <What do we call this?>
      /* Request quit when it's safe.  */
      int count = NILP (Vquit_flag) ? 1 : force_quit_count + 1; <-- on text terminals, Vquit_flag is nil!
      force_quit_count = count;
      if (count == 3)
	Vinhibit_quit = Qnil;
      Vquit_flag = Qt;
    }
  ...
}

So, on GUI frames, a double C-g will leave force_quit_count set to 2,
but it will not perform an Emergency Escape, and it will not clear
inhibit-quit.  Nothing special happens when force_quit_count reaches 2.

The third C-g, assuming quit-flag is still set, will clear inhibit-quit,
set the quit-flag again, and thus trigger a "special kind of quit".  I'm
looking for a name for it.

I note that this triple-C-g behavior is documented only in the code;
it's missing from trouble.texi.

diff --git a/doc/emacs/trouble.texi b/doc/emacs/trouble.texi
index 45f15c7dd0d..33c97352f7c 100644
--- a/doc/emacs/trouble.texi
+++ b/doc/emacs/trouble.texi
@@ -83,6 +83,10 @@ Quitting
 actually executed as a command if you type it while Emacs is waiting for
 input.  In that case, the command it runs is @code{keyboard-quit}.
 
+  On graphical terminals, you may be able to quit by typing @kbd{C-g}
+three times, even when an ordinary @kbd{C-g} doesn't work.
+@xref{Feature X}.
+
   On a text terminal, if you quit with @kbd{C-g} a second time before
 the first @kbd{C-g} is recognized, you activate the emergency-escape
 feature and return to the shell.  @xref{Emergency Escape}.
@@ -157,7 +161,8 @@ Lossage
 * Memory Full::           How to cope when you run out of memory.
 * Crashing::              What Emacs does when it crashes.
 * After a Crash::         Recovering editing in an Emacs session that crashed.
-* Emergency Escape::      What to do if Emacs stops responding.
+* Feature X::             What to do if Emacs stops responding.
+* Emergency Escape::      What to do if Emacs stops responding on a text terminal.
 * DEL Does Not Delete::   What to do if @key{DEL} doesn't delete.
 @end menu
 
@@ -400,6 +405,22 @@ After a Crash
 which does not already exist; if the file does exist, the script does
 not make a backup of its old contents.
 
+@node Feature X
+@subsection Feature X
+@cindex feature x
+
+@c This works only if all terminals are graphical terminals; mixed
+@c text/GUI terminal sessions use the Emergency Escape feature.
+
+  On graphical terminals, typing @kbd{C-g} three times in a row while
+Emacs is not responding will clear the @code{inhibit-quit} flag.  This
+allows Emacs to recover from being stuck in certain kinds of loops.
+
+  After using this feature, Emacs will appear to have processed an
+ordinary quit, but you should exit and restart Emacs.  This is because
+forcibly clearing the @code{inhibit-quit} flag may have left Emacs in an
+inconsistent state.
+
 @node Emergency Escape
 @subsection Emergency Escape
 @cindex emergency escape

Obviously, we should replace "feature X" by something else.

Pip