GNU bug report logs - #78737
sit-for behavior changes when byte-compiled

Previous Next

Package: emacs;

Reported by: Daniel Colascione <dancol <at> dancol.org>

Date: Mon, 9 Jun 2025 20:50:02 UTC

Severity: normal

Full log


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?




This bug report was last modified 55 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.