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

From: Daniel Colascione <[email-masked]>
Date: Wed, 11 Jun 2025 09:35:48 -0700

Eli Zaretskii <[email-masked]> writes:

>> From: Daniel Colascione <[email-masked]>
>> Cc: [email-masked],  [email-masked],  [email-masked]
>> Date: Wed, 11 Jun 2025 07:57:52 -0700
>> 
>> Eli Zaretskii <[email-masked]> writes:
>> 
>> > Anyway, I think arguing about this aspect is not useful.  My problem
>> > is not theoretical, it is practical: how much will break due to these
>> > changes, and how long will it take us to become aware of the breakage
>> > and attempt fixing it?
>> 
>> I went through all the uses of read-char, read-char-exclusively, and
>> read-event in the tree and didn't find anything that'll break if we make
>> these functions return C-g as an event.
>
> How did you look for potential problems?  E.g., the C-g flow is
> impossible to see in the code, you need to try it; doing that in all
> the possible places where we use this stuff and with all the different
> timings is practically impossible, I think.

I tried everything (e.g. kbd-macro-query) where the effect wasn't
obvious from the code.  And I'm not sure what "timings" you're talking
about here: read-char, etc. will now _deterministically_ return \?C-g on
quit, so there's no timing involved.

For the broader change to quit, well, I've been using the thing and have
seen no problems.  isearch works fine.  Macros work fine.
minibuffer-quit in a macro works just as it did before too.

Input methods work fine --- at least greek, russian-typewriter, and
Chinese 4-corners.  Any others you think might be particularly hairy?

With the exception of read-char, read-char-exclusively, and read-event,
the Lisp API contract hasn't changed: wherever we would previously run
the command bound to C-g most of the time, we now run all the time.
That's not going to break anything: we're merely taking something that
was 1) supposed to happen, and 2) usually did happen, and make it happen
in all the cases users expect it to happen.  Keep in mind that you've
always been able to bind commands to C-g: it's just that previously, C-g
would occasionally fail to invoke these commands.  Now it does.

Quit in redisplay is now inhibited by default, but you said that was a
bad idea to allow in the first place.

Lisp quits work normally.  Better, actually: the NS port now even
reliably quits where it's supposed to quit.  The debugger works fine.
edebug works too.  debug-on-quit works.  Recursive edits ---
no difference.

The C-g eating test I've been using is to start a (compile "cat
/dev/urandom") and try to interact with Emacs.  Previously, the Emacs
command loop would eat tons of C-g presses as quits during this stress
test instead of running the command bound to C-g: now we reliably run
the bound command.  I've run the same test under a TTY too.  Works fine.

Transient, execute-extended-command, and other things that bind commands
to C-g all work fine and more reliably.  Quitting in minibuffer read
still works fine, even when we have a message popup like "complete but
not unique".

sit-for works fine.  while-no-input works fine: better now that it
doesn't drop events.

I'm really not seeing what there is to be worried about.  I'm especially
not worried about timing considering that we've made the system more
deterministic and reduced the number of timing-related bugs we might
have had.

I'll add the kill switch you wanted.  I don't think it's necessary, but
it's not going to do any harm.  Other than this kill switch and making
sure SIGUSR2 quits out of read-event, it seems fine to me.

>
> The number of possible combinations of affected APIs with
> while-no-input and other stuff sensitive to this is also quite close
> to infinite.  And then there are keyboard macros, input methods (which
> not all of them work the same with unread-command-events etc.) and
> more.
>
>> > And I know what will break.  As I told, we don't have a good set of
>> > tests for it.  I only know that every time we changed something in
>> > read_char and its ilk in recent years, we ended up with regressions.
>> 
>> All the more reason to simplify its contract.
>
> If simplifying the contract removes support for valid use cases

It doesn't remove support for any use cases.  It regularizes the API and
the implementation.  There are no lost capabilities.  The contract of
read_char (the C function, not to be confused with read-char the Lisp
function, which is confusingly totally different) has changed to not
internally quit, bu.

> we
> break something, and need to somehow restore it (after we discover
> what broke, which might take time).

One can imagine unlimited unknown unknowns.

> And who knows what new complexity
> that will bring us.  But even if overall it means simplification, the
> energy invested in that is not free, and could be used in other areas
> of Emacs, where ROI is higher.

Energy isn't fungible.

>> > I agree, but when dealing with old and very complex code that
>> > thousands of programs rely upon, we need to consider the risks of
>> > changing the old behavior even if it is somewhat inconsistent.
>> > Because sometimes an old, known, and minor problem is better than new,
>> > unknown, and exciting ones.
>> 
>> Yes, and for cases in which we're changing user-visible semantics in a
>> way that'll break workflows --- like beginning-of-defun, perhaps ---
>> then I agree with you.  I don't think this particular change belongs to
>> that category.
>
> Not sure I follow: response to keyboard input and C-g is pretty much
> in users' faces.  Changes in it could well break workflows, when C-g
> has special meanings, like in C-s.

The only thing users will notice is Emacs behaving more predictably, and
in the case of the NS port, being more responsive to quits.  We're not
talking about something in users' faces.

>> I've looked for evidence that would change my mind and
>> haven't found any yet.  You're right that changes to the input loop are
>> risky, but they're going to be necessary sooner or later anyway, so
>> wouldn't you prefer to change simpler code?
>> 
>> Along the same lines, we could get rid of getcjmp too.  I'm not afraid
>> of rationalizing the contract of read-event or tweaking any other part
>> of keyboard.c, but that thing and quit_throw_to_read_char are extremely
>> confusing and do scare me a bit --- all the more reason, in my mind, to
>> get rid of them, like Gerd wanted to do years ago.  When is the right
>> time to do that?  It's not like an Emacs 31 release is imminent.
>
> It depends how you interpret "imminent".  Once Emacs 30.2 is released
> (which should be soon), the emacs-31 release branch will be cut soon
> afterwards, and development of Emacs 31 is basically frozen after
> that, only bug fixes and last-minute changes of existing features are
> allowed.  Fingers crossed, that should happen in 3 or 4 months.  The
> release itself is probably at least 6 months after that, but we can no
> longer make breaking changes during that period.

3-4 months is plenty of time for testing.  If it doesn't smell right by
then, it doesn't have to be in Emacs 31.