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

From: Eli Zaretskii <[email-masked]>
Date: Fri, 13 Jun 2025 09:25:36 +0300

> From: Daniel Colascione <[email-masked]>
> Cc: Eli Zaretskii <[email-masked]>,  [email-masked],
>   [email-masked]
> Date: Thu, 12 Jun 2025 11:48:50 -0700
> 
> Pip Cet <[email-masked]> writes:
> 
> > 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)))
> 
> I strongly disagree.  read-event should read an event.
> Setting quit-flag by side effect when it happens to read one key and not
> others makes the interface less regular and understandable.

We should start by agreeing that the capability of interrupting a
running Lisp program is a real need.  Are we in agreement about that?
If not, let's first hear the arguments why not.

If we _are_ in agreement about that, we should discuss how this should
be possible if read-event (and perhaps others?) return C-g instead of
raising quit-flag.  The alternatives mentioned until now are:

 . restore the old behavior, whereby C-g interrupts read-event
 . have a variable that, if set, will restore the old behavior in
   read-event and other affected primitives, to be interruptible by a
   single C-g
 . make two C-g presses "in quick succession" set quit-flag, IOW
   "C-g C-g" will have the same effect as C-g previously

Are there other alternatives?

> read-key-sequence is the high-level function.  read-event is the
> low-level function.  It makes zero sense for the high-level function to
> report a key event as a low-level event and for the low-level function
> to do special non-local flow control in response to that event.
> 
> > quittable, as I naively expected it to be.
> 
> The naive expectation is that this function do its job.

I don't think there's disagreement on that level.  So let's drop this
kind of arguments, because they are not useful for this discussion.
The problem we face is how to allow Lisp code to be quittable.  A Lisp
program that calls read-event is not different from a Lisp program
calling any other function, so we still need such programs to be
quittable somehow.  Let's discuss how best to do it, okay?

> > 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).
> 
> Get rid of force_quit_count entirely and just detect (by writing into a
> ring buffer) whether we've received N quits in the last T milliseconds.
> That'll work the same way regardless of how quits gets detected.  We can
> change N and T freely.

This is the last alternative I described above.  It is IMO less
desirable, because it is not always easy to press C-g twice quickly,
for whatever reasons.  It is even less desirable to define "quick
succession" in terms of time, because timings can differ a lot
depending on the free computing resources and the CPU power in
general, so determining the best default values will be very
challenging, to say the least.

> On your Emacs, you can set N to one and T to zero.

If read-event still returns a keyboard input event when C-g is
pressed, I don't see how N = 1 would work.  Can you describe how it
would work?

> >> Also, can this behavior be optional, like debug-on-error and friends
> >> are?  Not everyone debugs Lisp code all the time, so we definitely can
> >> have an "easy-break-out" feature that is by default off.
> 
> > Absolutely.  We could easily make it customizable whether read-event
> > sets quit-flag after a quit:
> >
> > 1. never
> > 2. only when !inhibit-quit
> > 3. always
> 
> We can customize thresholds for general behavior, but I don't think we
> should not have preferences that alter the operation of fundamental
> Emacs primitives.  You couldn't add a preference that made if regard 0
> as well as nil as false, would you? 

Why not?  If it helps debugging, we could definitely do that.  We
already have --enable-checking, which changes how some primitives
work, in a very real sense.  As long as the feature is for debugging
Emacs, anything useful goes, IMO.

> >>> Maybe a compromise would be to continue the arms race and downgrade C-g
> >>> to normal input, C-g C-g C-g to a quit, and require even more C-g's for
> >>> a force-quit?
> >>
> >> That's also possible, though less desirable: counting C-g presses when
> >> you are desperate is not easy and we cannot rely on users to do that
> >> reliably.
> >
> > And we'd need a way to detect when a quit is handled (however we define
> > "handled") so we could reset force_quit_counter.  Not a trivial change.
> 
> You don't.  You just upgrade any quit that happens under the N and T
> threshold above.

I'm skeptical that we will be able to count C-g presses so as to
reliably differentiate between double or triple press and two or three
separate C-g presses.  Different machines and OSes, and different
system loads, can make it nigh impossible to do reliably.  And that's
_bad_, because when I want something interrupted right away, I don't
want or even cannot try again and again and again until it works.