Package: emacs;
Reported by: Daniel Colascione <dancol <at> dancol.org>
Date: Mon, 9 Jun 2025 20:50:02 UTC
Severity: normal
Message #140 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: Thu, 12 Jun 2025 12:07:32 -0700
Eli Zaretskii <eliz <at> gnu.org> writes: >> Date: Thu, 12 Jun 2025 11:04:06 -0700 >> From: Daniel Colascione <dancol <at> dancol.org> >> CC: pipcet <at> protonmail.com, monnier <at> iro.umontreal.ca, 78737 <at> debbugs.gnu.org >> >> >> >> On June 12, 2025 9:56:26 AM PDT, Eli Zaretskii <eliz <at> gnu.org> wrote: >> >> Date: Thu, 12 Jun 2025 09:52:22 -0700 >> >> From: Daniel Colascione <dancol <at> dancol.org> >> >> CC: monnier <at> iro.umontreal.ca, 78737 <at> debbugs.gnu.org >> >> >> >> >> >> >> >> On June 12, 2025 9:32:26 AM PDT, Eli Zaretskii <eliz <at> gnu.org> wrote: >> >> >> Date: Thu, 12 Jun 2025 13:58:51 +0000 >> >> >> From: Pip Cet <pipcet <at> protonmail.com> >> >> >> Cc: Eli Zaretskii <eliz <at> gnu.org>, monnier <at> iro.umontreal.ca, 78737 <at> debbugs.gnu.org >> >> >> >> >> >> 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? >> >> > >> >> >Maybe this regression should be fixed, then. >> >> >> >> It's not a regression. It's a bug fix. Not every behavior change is a problem. Who starts coding something by calling it in a loop? That's like learning to drive by crashing into a wall. >> > >> >Did it never happen to you that you wrote a loop and forgot the part >> >that advances the counter or some other thing that will prevent an >> >infloop? Stuff happens when developing code. >> >> And the mechanism I described just now addresses the problem of recovering from programmer error. > > Sorry, I must have missed it (assuming that was in your previous > message). Could you please point me to that description, or repeat > it? It's near the bottom of https://lists.gnu.org/archive/html/bug-gnu-emacs/2025-06/msg00629.html > Specifically, what I'm interested in is how come > > (while t > (read-event)) > > cannot be interrupted by C-g, but you seem to be saying that > > (while t > (let (evt (read-event)) > (do-something-with evt))) > > _can_ be interrupted? (read-event) returns \?C-g. If the C-g arrives when Lisp code is running (e.g. inside (do-something-with evt), then we quit --- unless that code is also reading an event, in which case we return it. > Let's say when I type C-g, the loop is stuck > inside read-event: could you then describe how this latter loop could > be interrupted in that case? As I mentioned just now in yet another message, we impose a threshold. N quits in T milliseconds -> upgrade quit to immediate-quit when we Fsignal, with quit on immediate-quit's condition list, just like we do for minibuffer-quit. immediate-quit means "I definitely meant to exit whatever I'm doing, even if the code says to read an event or key sequence". N_emergency quits in T_emergency milliseconds -> do same as above, except that we ignore inhibit-quit when deciding whether to signal: /* Have we gotten at least N quits in last T milliseconds? */ static bool check_recent_quits (int n, int t) { return <whether we've received N_emergency quits in last T_emergency milliseconds>; } void probably_quit (void) { specpdl_ref gc_count = inhibit_garbage_collection (); /* Quit promptly if processing pending signals makes us want to quit. */ if (!QUITP && pending_signals) process_pending_signals (); if (QUITP || check_recent_quit(quit_emergency_count, quit_emergency_window)) process_quit_flag (); unbind_to (gc_count, Qnil); } and then, Lisp_Object quit (void) { bool upgrade = check_recent_quit (quit_upgrade_count, quit_upgrade_count); /* Only non-upgraded quits count as continuable. */ return signal_or_quit (upgrade ? Qquit_immediate : Qquit, Qnil, !upgrade); } and then in read_char, we change from if (convert_quits && !NILP (Vquit_flag)) { Vquit_flag = Qnil; result = make_fixnum (quit_char); } to if (convert_quits && !NILP (Vquit_flag) && check_recent_quit(quit_upgrade_count, quit_upgrade_count)) { Vquit_flag = Qnil; result = make_fixnum (quit_char); } and then... DEFVAR_INT ("quit-upgrade-count", quit_upgrade_count, doc: /* Number of quits received in quit-upgrade-period to count as an urgent quit that quits out of functions meant to read input events. */) quit_upgrade_count = 3; DEFVAR_INT ("quit-upgrade-period", quit_upgrade_period, doc: /* Time in which if we see at least quit-upgrade-count quits we quit out of functions meant to read input events. */) quit_upgrade_period = 500; DEFVAR_INT ("quit-emergency-count", quit_emergency_count, doc: /* Number of quits received in quit-emergency-period after which we disable safeguards that normally prevent quitting from protected code paths. */); quit_emergency_count = 6; a DEFVAR_INT ("quit-emergency-period", quit_emergency_period, doc: {you get the idea}); quit_emergency_period = 1000; At the top of command_loop_1, we reset the quit ring. The above thresholds count only between command invocations. TIMTOWTDI of course. This is a sketch. But something like this should make us both happy, yes?
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.