Package: emacs;
Reported by: Daniel Colascione <dancol <at> dancol.org>
Date: Mon, 9 Jun 2025 20:50:02 UTC
Severity: normal
Message #299 received at 78737 <at> debbugs.gnu.org (full text, mbox):
From: Lynn Winebarger <owinebar <at> gmail.com> To: Daniel Colascione <dancol <at> dancol.org> Cc: 78737 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>, Pip Cet <pipcet <at> protonmail.com>, Stefan Monnier <monnier <at> iro.umontreal.ca> Subject: Re: bug#78737: sit-for behavior changes when byte-compiled Date: Fri, 13 Jun 2025 17:55:49 -0400
On Fri, Jun 13, 2025 at 10:02 AM Daniel Colascione <dancol <at> dancol.org> wrote: > > > > On June 13, 2025 6:42:58 AM PDT, Daniel Colascione <dancol <at> dancol.org> wrote: > > > > > >On June 13, 2025 5:23:34 AM PDT, Lynn Winebarger <owinebar <at> gmail.com> wrote: > >>On Fri, Jun 13, 2025, 2:26 AM Eli Zaretskii <eliz <at> gnu.org> wrote: > >> > >>> > 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. > >>> > >>> 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 > >>> . have a variable that, if set, will restore the old behavior in > >>> read-event and other affected primitives, to be interruptible by a > >>> single C-g > >>> . 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 > >>> > >>> Are there other alternatives? > >>> > >> > >>What about keeping a (possibly buffer-local?) lisp variable holding a list > >>of keystrokes mapped to thunks that are treated as generating lisp machine > >>"interrupts"? The key strokes would be processed by C machinery and never > >>seen directly by lisp code and not be considered "events". > >>Then C-g could be bound to a thunk signalling quit, and the effect of > >>"inhibit-quit" achieved by removing C-g from the list in a given dynamic > >>scope. Then user code could make other key-strokes "special" without > >>resorting to read-event. For example, this read-event call in term.el: > >>(message "Hit space to flush") > >> (let ((ch (read-event))) > >> (if (eq ch ?\s) > >> (set-window-configuration conf) > >> (push ch unread-command-events))) > >> > >>Could be replaced by something like > >>(with-interrupts ((?\s (signal term-flush))) > >> (condition-case nil > >> (while t (sit-for 100)) > >> (term-flush (set-window-configuration conf)))) > >> > >>Then some of these use-case concerns could be mooted altogether. > > > >We already have something like that. :-) read-event already runs the events it reads through special-event-map, right? We don't even need to create a separate thunk list concept: we could just bind C-g in special event map and do what we want, right? Would that provide the behavior Pip is looking for? I can't pretend to understand what is going on in read_char, but it looks like that map isn't applied to *every* key entered, to ensure they are removed from the normal event stream. > >The only special thing about C-g is how we treat it when Lisp is running. When it's instead reading an event, it can and be a boring event processed the same way every other event is. It seems to me what Pip is looking for is analogous to a "Quit" button hardwired to a pin on a CPU, which is not something that can be reliably implemented in the C primitives as they are now. That would mean adding "interrupts" to the lisp machine architecture. If I think about it seriously, I guess I would be talking about a mechanism incorporated into signal handlers that would do surgery on the appropriate thread's C/Lisp machine stack so that when that machine's operation is resumed, the active frame would be for a call to the registered interrupt handler, arranged to return to cleanly return to wherever the C code was before the signal, unless the interrupt handler performed a non-local exit, like (signal 'quit). Maybe the interrupt handler shouldn't be a thunk, but take two arguments: the data delivered with the signal (in this case the keystroke) and a timestamp, so your "<N> quits in <M> milliseconds" could be implemented when there are multiple keystrokes before the lisp thread resumes. In this scenario, the signal handler would never deliver "hard-wired" interrupt keystrokes to the event queue processed by read_char. It would also mean a C-g (or other designated interrupt) could be processed earlier than preceding keystrokes, but that seems like what Pip would want? Theoretically, such interrupt handlers could be required to be byte-code functions that don't use any operations that could produce unbounded stack usage, most notably no call instructions, or certified by the native compiler to be "safe" for use with interrupts. Also, byte-code ops for "signal" and "throw" would need to be added. The possibility of forcing a non-local exit on the lisp thread would be the whole point of arranging a handler to run on that thread in the first place. Anyway, this kind of restriction would probably scare-off any non-serious uses of the facility. This approach is really too low-level for the use I suggested in term.el, though I do think there should be some way of saying "wake me up when this key is pressed" and either block until that happens (queueing other keystrokes) or let the command loop do whatever it normally would if that makes sense. > Oh yeah, one thing I forgot to mention: for the *asynchronous* case, the C code is pretty deeply coupled to quit_char, and IIRC, even hard codes 7, C-g, in some places instead of quit_char. In your proposal, we'd references to quit_char and literal 7 to calls to a new bool is_quit_char(int c) function -- and this function would return true for any raw keys bound in special-event-map. In this way, you'd be able to define as many asynchronous-input characters as you wanted. I guess I would consider removing that kind of hard-coded logic a plus? > > We wouldn't get the special SIGINT behavior this way (because in termios there can be only one interrupt key, right?), but I think that's okay, since quitting works without the SIGINT wiring and I suspect that we could remove the SIGINT wiring and simplify the codebase without losing any actual functionality. I suppose I am thinking from the perspective of making the lisp machine more machine-like, to facilitate the possibility of running multiple elisp "cpus" or "cores" in the same process, but that's all pie in the sky at this point. I'd like to see inputs and displays become clean sources and sinks, with the threads processing the data freed from concerns like whether redisplay will be called. Like having dedicated coprocessors for I/O devices, with the lisp machine core primitives just concerned with the data structures (like buffers) they manage. I'm talking awfully big for someone struggling to make read0/readevalloop re-entrant enough to interleave progressive evaluation of forms in a string with loading a file or evaluating a buffer. Lynn
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.