emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: question about `sit-for' and `C-g'


From: Davis Herring
Subject: Re: question about `sit-for' and `C-g'
Date: Mon, 14 May 2012 11:39:47 -0600
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.18) Gecko/20110717 Lanikai/3.1.11

[My point-by-point reply, proved quite repetitive, so here are some
general points for later reference:]

0. From the standpoint of a user or Lisp programmer, Emacs is at any
moment either running a command or waiting for one (a key sequence).
These two states nest via recursive editing levels (and use of the
minibuffer is one such); only the top-most level counts.

1. The normal behavior of C-g, while a command is executing, is to
destroy that command and return to the wait that preceded it (printing
"Quit" as feedback).  (Internally, this is because any command loop
catches all signals, including the quit that destroys the command.)

2. You are expected to know #1 (and thus #0 to understand it), and as a
Lisp programmer to know that it applies to your programs in the absence
of special requests (`with-local-quit', for instance).

3. If you don't know #1, feel free to call that a documentation bug.
"Patches welcome."

4. The behavior you observed was this normal behavior; your command
(that ran `sit-for') was destroyed, and Emacs resumed waiting (in the
minibuffer) for further input.  `sit-for' was entirely irrelevant -- you
would have seen the same, normal behavior had your code been in a long
computation loop at the time.  `sit-for' does not have "special
behavior" with regards to C-g.

5. From #4, if `sit-for' is insufficiently documented, so are `let',
`while', and `funcall' (which are the common places a quit is checked).
 `sit-for' deals with input and they do not, but they treat C-g
identically (that is, not as input).

6. #1 is not special; there are other general concepts that are not
mentioned everywhere they have effect.  Keyboard macros can provide
input anywhere (but `read-event' doesn't mention them), advice can
change the meaning of any function (but `funcall' doesn't mention it),
etc.  Documenting every global feature everywhere is counterproductively
noisy.

> And I do want `C-g' to be handled by its current keybinding for each call to
> `sit-for' in my code.  For both of these reasons it makes sense to bind
> `inhibit-quit' in `icicle-sit-for'.]

Sure -- my point was that you need not duplicate the implementation of
`sit-for' merely to bind `inhibit-quit'.

> And searching Emacs Lisp and C source code for `ding', `beep', SIGQUIT, and
> "Quit" showed that such occurrences (and there are a _lot_ of them) are not
> involved either.
> 
> In sum, it was not easy to find what was handling the `C-g' in this situation.
> I finally tracked it down to a call to `sit-for' and thus ultimately to C
> function `read_char'.
> 
> That call to `sit-for' was so ancillary to the general user interaction in the
> minibuffer that I did not even think of it at first.  It was only after
> searching unsuccessfully for what code might be intercepting the `C-g' and 
> just
> printing `Quit', and a fair amount of debugging (including via
> `read-bell-function), that I eventually discovered the `sit-for' and its `C-g'
> gotcha.

#2, #4

> "Aborts the currently running Lisp code" does not, in this case, mean that it
> returns to top level.  It seems to mean that it aborts `sit-for' (?).

#1

> Or rather, `read-event' & compagnie, while reading keyboard input, do not seem
> to just treat `C-g' as an ordinary input character, but rather they handle it
> directly (in addition to returning it).  They seem to "cause a quit", AFAICT.

#4

There actually is special (relative to the rest of Emacs) behavior here:
if `quit-flag' is non-nil during `read-char', it returns `quit_char'.
If `inhibit-quit' is also non-nil, `quit-flag' is cleared (since the
quit has been "consumed" as input); otherwise the quit remains
outstanding and the signal will be raised soon thereafter.  (This is
arguably a bug: (setq n (read-event)) actually sets n to 7 on C-g
because the `setq' happens before the quit is signaled.)

However, leaving aside the delayed quit, this is invisible to the Elisp
programmer except that "when `inhibit-quit' is set, `read-event' can
read C-g", which is hardly counterintuitive.

> Or maybe I'm not paying close enough attention to "command loop" here, and
> focusing on "while ... waiting for keyboard input".  IOW, maybe I'm trying to
> read more (or less) into this passage than was meant.

#1

> Binding it would, and does, simply cause `C-g' in the minibuffer to do what
> `C-g' in the minibuffer does in general and is meant to do there:
> `abort-recursive-minibuffer'.  TRT?
> 
> Not binding it causes `C-g' to just beep and print `Quit', which is not really
> what a user wants, IMO.  In that case, the `C-g' does not really quit 
> anything,
> AFAICT, except perhaps the `sit-for' itself.
> 
> What a user expects when s?he uses `C-g' in the minibuffer is
> `abort-recursive-edit', IMO.  S?he does not know or care whether a `sit-for'
> might be in progress.

#0  (The user must care because they must know whether a command is
running.  If they want to kill the minibuffer, they know that's two
levels and so two `C-g's.)

If you're writing a command that pretends to not exist so that keys that
arrive during its execution are treated as they would be were it not
executing, yes you will have to do strange things like bind
`inhibit-quit', set `unread-command-events', etc.  This should not be
surprising, as you are writing a command that needs to operate outside
the standard rules.

> Maybe it's as simple as asking whether `C-g' during `sit-for'
> should quit only the `sit-for' (?), i.e., whether that is ever useful.

Then a loop
  (while ... (do-something-inexpensive) (sit-for 1))
would be difficult to interrupt because nearly every C-g would hit a
`sit-for'.

> More importantly, let me just say that it took me a few _hours_ to figure out
> what was going on.

Unfortunate that it was so painful: #3.

> Had _any_ of the functions I came across in my quest documented that it 
> handled
> `C-g' directly as a special case (preventing it from being handled in the
> caller, via its key binding, etc.), then I would have found the answer
> (understood) immediately.

#5, and #6.

> (By "special case" I mean that it is treated there differently from other
> events/chars that are read.)

#1: this is not a special case, any more than ^C is a special case when
a program is reading from the terminal.  It kills the process whether or
not it's reading at the time.

> Looking at the code for `sit-for', unless you happen to know that `read-event'
> handles `C-g' directly, you (I) can easily get the impression that the `C-g'
> char gets pushed onto `unread-command-events'.  (And that's the behavior I 
> would
> in fact expect/prefer.)

#2

> So yes, I disagree that this should not be documented - and yes, at the
> individual function level.  It certainly does not hurt, IMO, to let users of a
> function know that a `C-g' char is handled specially there, even if this is, 
> as
> you seem to say, something that people should know generally from reading 
> (elisp
> `Quitting').

#5, #6

> Anyway, I do appreciate your reply/explanation, Davis.

Glad to be of assistance!

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]