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

From: Pip Cet <[email-masked]>
Date: Fri, 13 Jun 2025 12:26:19 +0000

"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?

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.

I want to expand the number of situations in which a simple C-g quits,
and expand the number of situations in which a triple C-g force-quits.

(while t (read-event))

is quittable now, and should remain so, IMHO.

> The naïve expectation from this is that the code affected by
> inhibit-quit non-nil is _not_ quittable, no?

Indeed, but the code outside of the let, once per loop iteration, should
be.

>> 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.

>> 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.

> Or are you talking about the effect of the changes on the branch?

I'm not, no.

Pip