Package: emacs;
Reported by: Daniel Colascione <dancol <at> dancol.org>
Date: Mon, 9 Jun 2025 20:50:02 UTC
Severity: normal
View this message in rfc822 format
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: 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.
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.