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

From: Eli Zaretskii <[email-masked]>
Date: Fri, 13 Jun 2025 17:54:40 +0300

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

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

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