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

From: Daniel Colascione <[email-masked]>
Date: Fri, 13 Jun 2025 00:53:38 -0700

Eli Zaretskii <[email-masked]> writes:

>> Date: Thu, 12 Jun 2025 18:37:58 +0000
>> From: Pip Cet <[email-masked]>
>> Cc: [email-masked], [email-masked], [email-masked]
>> 
>> "Eli Zaretskii" <[email-masked]> writes:
>> 
>> > Please describe the situations where you'd like it to throw to top
>> > level and it doesn't now.
>> 
>> One situation for ordinary quits; three for force-quit.
>> 
>> Situation 1:
>> 
>> I'd like read-event, when called while inhibit-quit is t, to report
>> quits by setting quit-flag in addition to returning quit_char: it'll
>> simplify the C code, and it would make
>> 
>> (while t
>>   (let ((inhibit-quit t))
>>     (read-event)))
>> 
>> quittable, as I naively expected it to be.  The old behavior would
>> remain available, but require an extra let binding.
>
> But isn't it confusing that to have something quittable one needs to
> bind inhibit-quit non-nil?  The naïve expectation from this is that
> the code affected by inhibit-quit non-nil is _not_ quittable, no?
>
>> Note that this isn't
>> 
>> (let ((inhibit-quit t))
>>   (while t
>>     (read-event)))
>
> Which is also confusing by its inconsistency.  In general, the order
> of let-bindings doesn't matter.
>
>> Situation 3:
>> 
>> Several force-quits in the same session.  Reset force_quit_count to 0
>> once it reaches 3.  I've seen force_quit_count reach higher values than
>> 3 (there was no regular quit in between force quit attempts).
>
> If you are talking about a GUI session, then IME the 'emergency exit"
> procedure isn't reliably working in that case, and I'm not sure the
> implementation intends to support that.  I always knew that it's only
> reliably working on TTY frames.
>
> Or are you talking about the effect of the changes on the branch?

FWIW, the purpose of my N-times-in-T-milliseconds-within-one-command
formulation of emergency exit is to get the mechanism working reliably
in all cases.

I can definitely type 4-5 C-gs in a GUI Emacs (well, NS, but I recall
PGTK being similar?) and not have Emacs quit.  If I mash C-g, it
sometimes does, and sometimes doesn't.

Right now, the logic is this:

    {
      /* 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;
    }

IOW, the first quit after clearing Vquit_flag resets the count to one.
Maybe that's why it isn't working reliably right now.  If we reformulate
this mechanism not in terms of count == 3 (which is fiddly for all sorts
of reasons, since Vquit_flag can get reset) but in terms of the UX
directly --- N recent quits in T time in a single command --- we make
the whole thing more reliable.

If you set T=infinity and N=3, you get the current force quit UX (modulo
my upgrade-before-disabling-inhibit-quit thing), just more reliably, and
you can break out of arbitrary Lisp code.