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: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Daniel Colascione <dancol <at> dancol.org>, Eli Zaretskii <eliz <at> gnu.org> Cc: 78737 <at> debbugs.gnu.org, pipcet <at> protonmail.com Subject: bug#78737: sit-for behavior changes when byte-compiled Date: Fri, 13 Jun 2025 11:37:51 -0400
Ladies and gentlemen, please let's try and keep this civil. Let's remember that misunderstandings and implicit assumptions are part of human life. Stefan Daniel Colascione [2025-06-13 06:35:40] wrote: > On June 13, 2025 4:34:06 AM PDT, Eli Zaretskii <eliz <at> gnu.org> wrote: >>> From: Daniel Colascione <dancol <at> dancol.org> >>> Cc: pipcet <at> protonmail.com, monnier <at> iro.umontreal.ca, 78737 <at> debbugs.gnu.org >>> 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? >> >>All of them. > > Do you want a mechanism that can interrupt all Lisp programs or not? I have such a mechanism. > >>> 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. >> >>So because we currently cannot interrupt some programs we should give >>up the ability to interrupt all of them? > > My proposal allows us to interrupt all Lisp programs. I don't know how many > times I have to say this to get the point across. > >> Please be serious. > > Please engage with what I'm saying and at least try to say things that make internal sense. > >>Arguments like the above are a red herring, and don't help advancing >>this discussion towards some kind of agreement. > > You just said above that we should consider the interruptabiltity of all > Lisp programs. I cited examples of Lisp programs. That means these Lisp > programs are relevant to the conversation, as they, being Lisp programs, > belong to the category of all Lisp programs. > >>Do you want the branch merged at some point? > > Do you want bugs fixed and behaviors improved at some point? > >> Then please drop the >>attitude and start participating in the discussion seriously. You >>understand very well what I meant above, even though it is somewhat >>vague. > > Actually, I have no idea what you mean: you say things like you're not sure > whether we can distinguish two C-g presses from a double C-g press. There > are the same thing. It's like asking whether we can distinguish #00f from > #0000ff. > >> We all know what Emacs can and cannot do today, so I allow >>myself not to write too many well-known details. >> >>Let's consider the current capabilities of interrupting Lisp programs >>as base line, and try to maintain that base line, if not improve on >>it. Okay? > > I'm suggesting being able to interrupt all Lisp programs. Not all Lisp > programs can be interrupted today. I have explained in multiple places how > that is to be done. I am happy to explain further if you give me some > substantial technical questions about the mechanisms involved. > > When Lisp writes (while t (some-event-reading-function)), programmer intent > is not clear. It's not clear because C-g is overloaded and can be either an > event or a quit command. On the one hand, the Lisp is running Lisp and we > have a general contract that Lisp can be interrupted with C-g. On the other > hand, Lisp is asking to read an event, and C-g can be an event. We have to > resolve the ambiguity SOMEHOW. Right now, Emacs resolves the ambiguity in an > inconsistent and ad-hoc manner. If the some-event-reading-function above is > read-event, we resolve the ambiguity in favor of quitting. If instead > some-event-reading-function is read-key-sequence, we resolve the ambiguity > in favor of event reading. This inconsistency is real, confusing, and > illogical. In my proposal, we resolve the ambiguity in favor of event > reading and use multiple C-g presses to indicate user intent to quit instead > of provide input. I don't believe there's a practical, real world use case > in which a single C-g will be insufficient, but the ability to press C-g > multiple times will be there regardless. If you'd like to configure your > Emacs to resolve the ambiguity in favor of quitting, you can do that by > changing one variable. Either way, behavior will be consistent among all > Lisp functions that read events. > > Would you please engage substantively with the previous paragraph instead of > calling it a distraction and then raising points germane to exactly what the > previous paragraph covers? > > Again, nobody is talking about making users count C-g. Nobody is talking > about requiring multiple C-g presses in any real world case. The change I'm > talking about will make Emacs more consistent, and I've yet to see anything > in the real world it would break. > > My change also makes Emacs safer, because in my model, you *can* break out > of (while t (read-key-sequence)) and in master today you cannot. There will > be *one* uniform and consistent (and easy to understand) model for how we > address the *inherent* ambiguity about what control-g on a keyboard means. > > Or does it still not satisfy your intentionally-pedantic >>interpretation of what I write? > > I wouldn't have to be pedantic if you would be clear about what you want. > >>> > If we _are_ in agreement about that, 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. >> >>Please reconsider your responses with a more serious and cooperative >>attitude. > > Then make some sense. You talk about requirements. I explain how we meet > those requirements. That's pedantry? > >>> > . 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. >> >>Why "only"? >> >>Having the behavior vary depending on whether the program does or >>doesn't call read-event will bring inconsistency, something we want to >>avoid (and which I think you argued against). > > It is logically impossible to combine two things: > > 1) all Lisp programs can be interrupted with a single C-g > > 2) a Lisp program can read a C-g as an event. > > This isn't pedantry. It's foundational logic. Demanding a solution that > satisfies both constraints is impossible. It would require reading the > user's mind. > >>> 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. >> >>Not useful. I didn't mean that, and you know it. > > You just said at the start of this email that you're considering the > interruptabiltity of all Lisp programs. I have a scheme to make all of them > interruptible. Nobody else has proposed one. > >>> > Are there other alternatives? >>> >>> Get in a time machine, go back 40 years and stop overloading C-g? >> >>Even less useful. Again, do you want this branch to be merged any >>time soon? Because I'm this close to losing my patience. Life's too >>short to waste it on "arguments" such as this one. >> > > And I'm losing patience with objections that contradict themselves and fail to address my points. > >>> > 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? >> >>Why is that relevant? > > If you'd like to cooperate seriously, I'm happy to do so provided you do so > as well, and part serious cooperation is answering clarifying technical > questions instead of questioning their relevance. > > So, yes or no? > >> I asked about read-event, not >>read-key-sequence. Can we first discuss the read-event case? Once we >>are done with that, we can move to other cases. > > You just said above you want to consider the interruptabiltity of all Lisp > programs. Now you're calling the interruptabiltity of some Lisp programs > irrelevant. Which is it? > > It's important to consider the input model as a whole. I don't think we can > get to a good place by focusing on one function at a time and be wilfully > oblivious about whether different functions together form a coherent whole. > >>> 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 inhibit-quit is bound to a non-nil value, the program cannot be >>quit, and that's a feature. > > This seems like both a contradiction and poor UX: you want some Lisp > programs to be interruptible and others not. Why is it a feature that some > Lisp programs cannot be quit? I thought you wanted to talk about all Lisp > programs being quittable. Why is it desirable that Emacs be left in an > unrecoverable state? > > In my proposal, users will be able to interrupt *any* program of the form > (let ((inhibit-quit t)) (while t (anything))) by pressing C-g enough times > to override the inhibit-quit. Emacs already provides this feature, but only > (AFAICT) on primary TTYs. > > If this ability to break out of a loop wrapped in inhibit-quit is bad, > should we remove the existing force-quit feature? Yes or no, please. > > My proposal is a generalization of a feature we've had for decades. It is not a brand new concept. > > Is it a bug to allow these programs to be interrupted? Yes or no. If yes, > why isn't the existing force quit feature being removed? If not, why is > being unable to quit some Lisp programs a "feature"? > > You say I know what you mean. I'm really not sure I do, because from where > I'm sitting, you're not answering questions that would resolve the > contradictions in what you're saying. > >> Why are we discussing this? > > > Because we're talking about interrupting Lisp programs. That's the subject > of the whole conversation. > >> >>> 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? >> >>Again, let's discuss the read-key-sequence case after we are done with >>the read-event case. > > How can we consider what to do with read-event without thinking about how it > fits into the broader picture? > >>> >> > 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? >> >>I usually expect a single C-g to quit. If it doesn't help, I can >>press C-g several times, I'm not sure I count them. > > In my proposal, on every real world case, C-g returns you to the command > loop when you want to go there and lets Lisp read C-g as an event when you > want to do that. You can construct buggy or pathologically written programs > that you can't exit with a single C-g because the meaning of that keystroke > is overloaded. You can still get back to the main loop in these rare cases > by pressing C-g repeatedly. You don't have to count them. You just keep > pressing C-g until you are back in the command loop, just like today's force > quit feature. > >>> 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. >> >>See above. >> >>> We already have a force quit mechanism that kicks in at N=3. How often >>> do you activate it? >> >>I never saw it at work, except on TTY frames. > > We can make it work everywhere. Is that not an improvement? > >> But then Windows >>doesn't have SIGIO, it emulates that. Maybe that's the reason. > > SIGIO isn't the reason. The reason force quit doesn't work is the way it's > implemented. We can implement it better. That's my proposal, if you'd like > to consider it in more detail. > >>> >> 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. >> >>Sorry, I don't understand. If N = 1, what will read-event do when the >>user presses C-g while inside read-event? > > If N=1, it will quit after a single C-g. Specifically, it will raise a quit > signal. It will raise this signal instead of returning normally. It will not > return it as C-g to Lisp. If N=1, all the Lisp level input reading > functions, including read-key-sequence, will interpret a single C-g as quit. > >> Will it return the input >>event C-g, or will it quit? I thought your changes make read-event >>always return the input event, was I mistaken? > > You're asking about the N=1 case. I'm not proposing that N=1 be the default. > > For all values of 1 <= N <= M, I'm specifically proposing: > > When inhibit-quit is nil: > > #quits in [1,N-1]: event reading functions interpret C-g as an event called > \?C-g, aka the number 7. They return this event without quit-flag set. > > #quits in [N, infinity): event reading functions interpret C-g as > a quit. They do not return. They end by calling Fsignal with quit > error symbol. > > When inhibit-quit is non-nil: > > #quits in [1,N-1]: same as above > > #quits in [N,M-1]: return \?C-g with quit-flag set > > #quits in [M, anything): exit nonlocality by calling Fsignal with quit error > signal, effectively ignoring inhibit-quit. Print a message saying we've > done so. > > If debug-on-quit is set, we enter the debugger when the above table says the word "Fsignal". > > If throw-on-input, we raise quit on any input event whatsoever, never > returning to Lisp, no matter the value of #quits, N, or M. > > My proposal, by the way, does not change the meaning of quit-flag. > > If you find the above proposal unacceptable, can you please point to > a specific behavior it encodes and talk about what you'd rather it > do differently? > > Assume we can reliably detect #quits, please. I'm happy to talk about *how* > we detect repeated C-g, but the mechanism by which we do is independent of > rhe policy we want. > >>> >> 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. > > In the exceedingly rare case that it is important to allow one C-g instead > of C-g C-g C-g to break out of an read-event or read-key-sequence loop (and > I have never seen one not deliberately constructed) users can customize N to > be one. I anticipate exactly zero people going that, but if you want the > knob, we have it. > >> We >>> > already have --enable-checking, which changes how some primitives >>> > work, in a very real sense. > > I'm not aware of any interface contracts that checking changes. What might those be? > >> 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. >> >>No, people need not expect the code to operate the same under those >>special debugging-oriented values. We already have such features: >>debug-on-error = t might well cause some command cease working >>normally, and similarly condition-case-unless-debug > > Because I don't think this debugging mode is needed in any real world > scenario. Nobody I've seen infloops on read event outside a specially > crafted scenario. > > But if you want the knob to enable it, the knob is there in my proposal. Set > N=1. If you set N=1, then *both* read-event and read-key-sequence will quit > the first time you press C-g > >>So I think you are bothered by a problem that doesn't need to be >>solved. >> >>> >> >>> 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? >> >>I don't understand what you are saying here. Yes, there's no logical >>difference between these, which is why I'm saying that reliably >>detecting "double C-g" is a challenge. We already have that with >>double-mouse. The difference between double-mouse and "double C-g" is >>that with the latter you cannot afford low reliability: when you want >>to quit, you want to do it immediately, because the runaway operation >>you want to interrupt could be harmful. > > I'm honestly not sure what reliability problems you're talking about. We > count the C-g key presses since we last entered the command loop and since > T milliseconds ago. If you say T to be a month and N=2 then if you're in > a read-event or read-key-sequence loop, press C-g once, suspend your > computer for a day, and press C-g again, we'll interpret the second C-g as > a quit and break out of the loop. > > Can you please give me some specific and concrete scenarios you have in mind > that might clarify these reliability issues you're talking about? > >>> > 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. >> >>It's too late to define a separate key _instead_ of C-g. We could >>define a separate key _in_addition_ to C-g, but that doesn't solve the >>problem: people have C-g hardwired into their muscle memory, and it >>will take a lot of time for them to re-learn. Meanwhile, we get bug >>reports about C-g not working like it did before. > > No, we didn't get a bug report. We got a specially constructed program that > exercised a beneficial behavior change and called it a bug. I've yet to see > a real world problem. The person who constructed that program complained > that a loop that could be quit before was unquittable now. I am trying to > explain that it will, in fact, be quittable. > >>So minimizing the behavior changes is still a requirement, even if we >>add another key. And that's before we even consider what other key >>could serve that purpose, which is not a trivial problem to solve >>portably > > I'm not talking about adding another quit key. That's not the right > solution. I don't see a real world scenario in which multiple C-g doesn't > work well enough, and we have, what, decades of experience with this > mechanism on TTYs? Why is taking the force quit mechanism, fixing its > reliability issues, and generalizing it to GUIs suddenly a problem? > >>> 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. >> >>That's possible, but it is not the best alternative IMO, for the >>reasons I explained. > > I've yet to see you propose a different solution. All I've seen is > resistance to change whether it's beneficial or not. > > My proposal allows you to interrupt any Lisp program. In every real world > scenario, you can interrupt with one C-g. In pathological cases, you can > interrupt by pressing C-g multiple times. This mechanism is reliable.
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.