Mats> #10 0x080d4482 in assert_failed (file=0x82c38ff
"signal.c", line=730,
Mats> expr=0x82c39c0 "QUIT called from within
redisplay without being properly wrapped") at emacs.c:3892
Ben, would you please document the "lisp wrapping" stuff, or
tell me where it is already documented so I can think about
where the documentation might be more findable? The comments
in the source code are completely impenetrable (and don't
mention "wrapping", except in those error messages); it gives
no guidance about what wrappers to use where, or what options
to give them in any given circumstance.
"Unwrapped assert" is responsible for most of the core dumps
that get reported (the dialogs and widgets are rife with it),
but you and maybe Jerry are the only people who understand it.
The original motivation was that, once inside of the redisplay code, you
cannot throw out of the code without leaving redisplay in an inconsistent
state, likely to lead to a crash. Long ago, there was code that aborted if
you tried to Fsignal() in redisplay, hence eval within redisplay was not
possible. Occasional crashes were triggered by code that allowed for QUIT
inside of redisplay, when people pressed C-g at exactly the wrong time and
triggered Fsignal().
I fixed this so eval inside of redisplay, as long as you protected the eval,
and added the "wrapping" business to catch places that threw past redisplay.
I then expanded it to catch places where QUIT was called that could
*potentially* lead to throwing outside of redisplay. However, awhile later
in the process of debugging occasional, maddening crashes due to corrupted
redisplay structures, I eventually found that even checking for QUIT at all
(esp. under Windows, but also possible under X) inside of redisplay was a
no-no since redisplay cannot handle reentrancy. See @subsection Nasty Bugs
due to Reentrancy in Redisplay Structures handling QUIT.
The assert is telling you that there is QUIT checking inside of redisplay.
Every place that sets in_display (there's only one of them) also turns off
quit checking (begin_dont_check_for_quit()). However, there are some places
that explicitly turn quit checking on (begin_do_check_for_quit()), to allow
people to abort runaway Lisp code. Pre_activate_callback(), which
implements menu filters under X, does this to allow people to abort runaway
menu filters (custom is a nasty offender in loading lisp files from within a
menu filter) -- search for UNINHIBIT_QUIT. Evidently the menu filter is
getting called within redisplay, which is wrong, but I need to see the
backtrace. One "solution" would be never to uninhibit quit when inside of
redisplay, but before doing that I want to see why this is happening in the
first place.
Here is a comment that references this; see also the section "Critical
Redisplay Sections" inside of internals.texi:
/* Within the guts of redisplay, we are defenseless and cannot allow any of
the following to happen:
1) garbage collection
2) QUIT
3) any non-local exits
4) frame size changes
5) deletion of any buffers, windows, frames, etc.
6) modification of buffer text
7) reentrant entry of redisplay (see the stack trace above
generate_displayable_area())
The general reason is that the redisplay code is written to assume that
it is the only code running, and thus (a) cannot tolerate structures
changed out from under it (hence 1, 4, 5, 6, 7) and (b) at various points
within redisplay the redisplay structures may be in an inconsistent
state and there are no unwind-protects to clean the structures up in
case of non-local exit (hence 2, 3). Fixing redisplay to address these
issues is hard and perhaps not worth it (and might slow things down a
fair amount). We address 1, 4, 5 and 6 ourselves inside of
enter_redisplay_critical_section() by simply inhibiting them, but we
cannot handle 2 and 3, which must be handled at the actual point where
they may occur (especially, internal_equal() or any place that may call
Lisp), by wrapping the code in call_trapping_problems() or
call_with_suspended_errors(). [[ NOTE: We could address QUIT by
inhibiting
it but this would be anti-social because it would prevent the user from
interrupting any Lisp code called within the critical section. With the
call_*() wrapping, C-g will interrupt the Lisp code and throw back to
the innermost wrapping. ]] In fact we do turn off QUIT handling, since
it's just too dangerous otherwise. See below.
Code calling enter_redisplay_critical_section() must check for reentrancy
(#7) and take appropriate corrective action.
To help debug potential problems, we arrange (when
ERROR_CHECK_TRAPPING_PROBLEMS is set) to crash automatically every time
we execute QUIT or call Lisp code unless proper wrapping is in place, as
well as further checks when we actually Fsignal(), Fthrow(),
garbage_collect_1().
#### If a frame-size change does occur we should probably actually be
preempting redisplay. */