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

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

On June 13, 2025 7:54:40 AM PDT, Eli Zaretskii <[email-masked]> wrote:
>> Date: Fri, 13 Jun 2025 12:26:19 +0000
>> From: Pip Cet <[email-masked]>
>> Cc: [email-masked], [email-masked], [email-masked]
>> 
>> "Eli Zaretskii" <[email-masked]> writes:
>> 
>> >> (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?
>> 
>> I'm confused: the code above should be quittable whether or not the
>> "let" line is present, as long as the "let" line comes after the "while"
>> line: we unbind inhibit-quit after each iteration, and I'm expecting
>> Emacs to take this opportunity to quit.
>
>You are looking at this from the implementation POV, while I look at
>this from the user POV.  Telling users to bind inhibit-quit non-nil to
>make a program quittable will make very little sense to users.

I *am* talking about this from the UX POV, and nobody AFAICT is proposing a mechanism that involves a program becoming quittable when adding a t binding to inhibit-quit. I agree that's a bad idea. That's why nobody is suggesting it.

>> >> 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.
>> 
>> I don't see how it's inconsistent, sorry.  If I bind inhibit-quit and
>> keep it bound while clearing quit-flag, I get an unquittable loop.  If I
>> repeatedly bind and unbind inhibit-quit so it becomes nil once per
>> iteration, I should get a quit when it does become nil, after the
>> binding has been unwound but before the next iteration's binding goes
>> into effect.
>
>You are again looking at the implementation aspects.

How many times do I have to put the situation in terms of UX? I mention user experience over and over, yet I see you insist I'm not considering UX. What specific aspect am I not considering?

>
>> >> 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.
>> 
>> I'm talking about the GUI case, yes.
>> 
>> It seems like an oversight to me.  Two successive emergency quits don't
>> work if both of these conditions hold:
>> 
>> 1. there's no intervening regular C-g
>> 2. quit-flag is non-nil
>> 
>> So a recipe would be:
>> 
>> (let ((inhibit-quit t))
>>   (setq quit-flag t)
>>   (while t))
>> 
>> C-x C-e
>> C-g C-g C-g
>> C-x C-e
>> C-g C-g C-g
>> 
>> Expected outcome: two emergency quits
>> Actual outcome: one emergency quit, no further emergency quits possible.
>
>I suggest to leave the emergency exit feature alone for now, and focus
>on the interruptibility of Lisp programs first.  We have enough issues
>to agree on and fix there.

The emergency exit aspect of the proposal is *how* we ensure that all Lisp programs can be interrupted.  I am not proposing that we make some Lisp programs uninterruptible. I am not proposing some system for automatically killing Emacs. I am proposing a more robust version of the mechanics we already have. Normal users in ordinary use will not see a UX difference, except on NS, where quits will start working reliably where they don't today.