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

From: Daniel Colascione <[email-masked]>
Date: Thu, 12 Jun 2025 10:46:56 -0700

Pip Cet <[email-masked]> writes:

> 1"Daniel Colascione" <[email-masked]> writes:
>
>> Pip Cet <[email-masked]> writes:
>>
>>> The first thing I looked at was mouse-drag-secondary.  It breaks (not
>>> too badly: it just loses a quit event, but it's still an undesirable
>>> change in behavior).
>>
>> Recipe?  mouse-drag-secondary seems to be working all right for me, but
>> I never use the feature in anger so I might be missing something.
>
> Start dragging.  Quit.
>
> Result on your branch: drag ends, no quit happens
> Expected result: drag ends, quit happens

I'd classify that as code working better.  No user scenario has
"broken".  If we want to display a message that the drag has ended,
let's display a message saying that in particular.

> (I'm not sure about the behavior change of event-apply-*-modifier.  Maybe
> those functions should bind inhibit-quit around the read-event call so
> you can compose M-C-g as "C-x @ m C-g".  However, binding M-C-g is still
> a bad idea, because someone might type "ESC C-g" for it and that would
> cause a quit when in Lisp code.)

Users can do plenty of things that are bad ideas.

>>>> marginally better, as I mentioned in my previous message.  It's possible
>>>> something breaks, but I haven't seen evidence of breakage yet.
>>>
>>> I'd say breaking (read-event) called in a loop is bad enough, because
>>> how else are you supposed to start developing code which uses it?
>>
>> No worse than calling read-key-sequence.
>
> Yes, worse than that, if only because read-event is usually called
> within a loop, while read-key-sequence isn't.

If it were a problem, we'd have seen more things break.  What user
scenario is broken?

> I disagree with the implication that every piece of Emacs source code
> that is broken in some way justifies breaking other places in the same
> way.

It's not broken.

>>> Since we've progressed to testing the branch, with the implication being
>>> that we'll merge it soon, it may be too late to make alternative
>>> suggestions.  In case it's not, though:
>>>
>>> I think this discussion is concerned too much with what existing code
>>> will break if we change quit not to quit, not enough with how much more
>>> difficult it will be to develop code if we do, and not at all, so far,
>>> with what the advantages of handling quit in Lisp (if Lisp decides to
>>> handle it at all) are.
>>
>> Lisp can quit just fine.  What are you talking about?
>
> With your patches, Lisp now has the responsibility to handle quits in
> many more situations, such as when it calls read-event.

No, read-event _reads an event_.  It's not the job of read-event to also
quit.  Quitting is for breaking out of running code.  It's not a side
channel for general input.

>>> People who don't want quit to quit could then call (set-quit-char nil)
>>> or something similar and reuse the quit character for something else.
>>> Something like that should be the only way to disable this extremely
>>> useful feature, though.
>>
>> Huh?  Nobody's disabling quit.
>
> You are, for some Lisp programs.
>
> (while t
>   (read-event))
>
> is Lisp code which should be quittable.  On your branch, it's not
> quittable.  Thus, you've disabled quit in this situation.

This is a "doctor, it hurts when I do this" situation.  If we had a bug
causing (/ 8 2) to report 3 instead of 4, you could argue that we
regressed a function calling (and (= (/ 8 2) 3) (insert "blah")).

>>> Independently of all this, we should change our triple C-g detection to
>>> work in cases where a Lisp user or keyboard.c clears quit-flag without
>>> actually handling the quit.  If we change things so C-g is ordinary
>>> input, we can at least limit the damage and let people use triple C-g in
>>> the unquittable infloops that will result (triple C-g isn't safe and you
>>> should restart your Emacs session after using it, but that's less
>>> inconvenient than losing the entire session).
>>
>> The branch we're talking about doesn't stop C-g quitting Lisp.
>
> You're making Lisp programs (which use C subroutines) unquittable when
> they weren't before.  I did not say that means "stop C-g quitting
> Lisp".

You constructed an unrealistic program.  People do call (read-event) in
a loop, but they _do something_ with the event.  Otherwise, why would
they call it and not (sit-for) or something?

>> Have you gotten your branches mixed up?  You seem to be ranting about a
>> set of patches with little resemblance to the ones we're discussing.
>
>> when's the last time you read keyboard.c?
>
> What's the point of such personal attacks?  It's totally inappropriate
> to suggest I did not read the source code, regardless of whether it's
> correct or not.  And even if I misunderstood your patches, that tone is
> inappropriate.
>
> (It doesn't matter much, but I was correct in both of these cases: your
> patch disables quit in some (too many) situaitons, and we do longjmp
> from signal handlers in keyboard.c, as the code clearly states.)
>
>> We don't jump in signal handlers for input.  If we did, we'd have much
>> bigger problems.
>
> Of course we do.
>
> #0  0x00007ffff5bba260 in __longjmp_chk () at /lib64/libc.so.6
> #1  0x00005555555aa1b8 in quit_throw_to_read_char (from_signal=from_signal@entry=true) at keyboard.c:12425
> #2  0x00005555555aa24f in handle_interrupt (in_signal_handler=<optimized out>) at keyboard.c:12400
> #3  0x000055555570cfca in deliver_process_signal (sig=2, handler=0x5555556eab10 <handle_interrupt_signal>) at sysdep.c:1751
> #4  0x00007ffff5adaed0 in <signal handler called> () at
> /lib64/libc.so.6

In my testing, on macOS (even without a window system), I see this
happen _only_ inside pselect, which doesn't really count: that's a
singular known point, not arbitrary code.  That's correct, if ugly.

In this part of the code, there's no chance of a write to getcjmp being
torn: the system call functions as a memory barrier.

If we can jump through _arbitrary code_ on other platforms, that's a big
problem, since if I'm reading this right, we can call arbitrary Lisp via
Fdo_auto_save.  That would be dangerous and would always have been.

But even if we _could_ call through arbitrary code this way, we still
wouldn't tear getcjmp because we call pthread_sigmask (SIG_SETMASK,
&empty_mask, 0) after we set it, and the system call acts as a memory
barrier again.  (We don't handle this signal on non-main threads either
if I'm reading this right.)

But who knows?  This code is tangled and the safer thing would be to get
rid of signal handlers that do more than set flags.

> As for the rest of your email, you mention longjmp a few times.  We
> don't disagree on that point: if we can handle quits without using
> longjmp (including longjmp from signal handlers), we should do that.

We should just treat TTY quits like GUI quits and quits from secondary TTYs.