GNU bug report logs - #78737
sit-for behavior changes when byte-compiled

Previous Next

Package: emacs;

Reported by: Daniel Colascione <dancol <at> dancol.org>

Date: Mon, 9 Jun 2025 20:50:02 UTC

Severity: normal

Full log


Message #149 received at 78737 <at> debbugs.gnu.org (full text, mbox):

From: Daniel Colascione <dancol <at> dancol.org>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 78737 <at> debbugs.gnu.org, pipcet <at> protonmail.com, monnier <at> iro.umontreal.ca
Subject: Re: bug#78737: sit-for behavior changes when byte-compiled
Date: Fri, 13 Jun 2025 00:28:45 -0700
Eli Zaretskii <eliz <at> gnu.org> writes:

>> From: Daniel Colascione <dancol <at> dancol.org>
>> Cc: Eli Zaretskii <eliz <at> gnu.org>,  monnier <at> iro.umontreal.ca,
>>   78737 <at> debbugs.gnu.org
>> Date: Thu, 12 Jun 2025 11:48:50 -0700
>> 
>> Pip Cet <pipcet <at> protonmail.com> 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.

Which Lisp programs? One that calls (while t (read-event))?  One that
calls (let ((inhibit-quit t)) (while t (read-event))?  How about one
that calls (while t (read-key-sequence ""))?  How about one that calls
(let ((inhibit-quit t)) (while t (read-key-sequence ""))?  If you adopt
the tenet that Lisp programs always be uninterruptible, _something_ has
to change from the present, because 3/4 of my examples above cannot
be quit.

(Today's force quit doesn't count: it doesn't practically work outside
primary TTY.)

> 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

Does not satisfy the "lisp programs always be
interruptible" requirement.

>  . have a variable that, if set, will restore the old behavior in

Same as above.

>    read-event and other affected primitives, to be interruptible by a
>    single C-g

Same as above.

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

Only for read-event.

If you want to adopt a principled stance that every Lisp program be
interruptible, you necessarily, as a matter of logic, regard multiple
behaviors of current Emacs to be bugs worth fixing.

> Are there other alternatives?

Get in a time machine, go back 40 years and stop overloading C-g?

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

Yes or no: should (while t (read-key-sequence)) be quittable?

If yes, maintaining today's behavior isn't an option.  There are plenty
of other Lisp programs that cannot be quit --- even (let ((inhibit-quit
t)) (while t)) cannot be quit!

If no, what is the problem with cleaning up Emacs by regularizing how we
treat C-g as an event versus some kind of longjmp?

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

I don't see the ambiguity being a realistic problem.  How often do you
press C-g three times while a single command is running?

We're not talking about, say, six times in multiple rounds of M-x this,
select that, deactivate mark over here.  Those are multiple commands.
In between multiple commands, a C-g will cause a keyboard-quit and
you'll regain control over Emacs.  I'm asking, during _one_ command, how
many times you typically press C-g and _don't_ mean it as a quit.

We already have a force quit mechanism that kicks in at N=3.  How often
do you activate it?

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

It wouldn't.  Such a setting would prevent read-input returning C-g.
That shouldn't be the default.  Maybe some people would customize the
setting to make Emacs behave this way.  I would not.

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

Because when you add a user option, people expect code you write to
operate under any value of that option, increasing implementation
complexity because you have to account for the _possibility_ someone
might operate in a certain way.  Complexity is insidious, and the
solution to a technical problem is rarely to add yet another technical
knob nobody is going to realistically touch but for which logic
must account.

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

There is logically no difference between these two concepts.  A double
keypress *is* two keypresses in a certain window of time.  What other
concept could the combination of the words "double" and "press" convey?

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

Then define a separate key that means _only_ quit and that cannot be
bound to a regular command.  C-g can't be that command without breaking
existing code.

Treating repeated C-g presses made in a reasonable window of time within
the scope of a single command in such a way that we're almost certainly
not confusing this series of keystrokes with C-g-as-command input solves
the problem.




This bug report was last modified 56 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.