| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| 37.1 Signal Handling | ||
| 37.2 Control-G (Quit) Checking | ||
| 37.3 Profiling | ||
| 37.4 Asynchronous Timeouts | ||
| 37.5 Exiting |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Note: The code to handle QUIT is divided between ‘lisp.h’ and ‘signal.c’. There is also some special-case code in the async timer code in ‘event-stream.c’ to notice when the poll-for-quit (and poll-for-sigchld) timers have gone off.
Here’s an overview of how this convoluted stuff works:
signal_quit(), which invokes the standard Fsignal() code,
with the error being Qquit. Lisp code can establish handlers
for this (using condition-case), but normally there is no
handler, and so execution is thrown back to the innermost enclosing
event loop. (One of the things that happens when entering an event loop
is that a condition-case is established that catches all calls
to signal, including this one.)
Vquit_flag and used
this as an indication to call signal_quit(). What signal_quit()
actually does is set Vquit_flag to Qnil (so that we won’t get
repeated interruptions from a single C-g press) and then calls
the equivalent of (signal ’quit nil).
quit-flag. This allows users some level of
control over whether and when C-g is processed as quit, esp. in
combination with inhibit-quit. This is another Lisp variable,
and if set to non-nil, it inhibits signal_quit() from getting
called, meaning that the C-g gets essentially ignored. But not
completely: Because the resetting of quit-flag happens only
in signal_quit(), which isn’t getting called, the C-g press is
still noticed, and as soon as inhibit-quit is set back to nil,
a quit will be signalled at the next QUIT macro. Thus, what
inhibit-quit really does is defer quits until after the quit-
inhibitted period.
quit-flag is set to critical instead of to t. When QUIT
processes this value, it ignores the value of
inhibit-quit. This allows you to quit even out of a
quit-inhibitted section of code! Furthermore, when signal_quit()
notices that it was invoked as a result of a critical quit, it
automatically invokes the debugger (which otherwise would only happen
when debug-on-quit is set to t).
quit-flag gets set correctly,
but I began with a disclaimer stating that this was the old way
of doing things. What’s done now? Well, first of all, the SIGIO
handler (which formerly checked all pending events to see if there’s
a C-g) now does nothing but set a flag – or actually two flags,
something_happened and quit_check_signal_happened. There are two
flags because the QUIT macro is now used for more than just handling
QUIT; it’s also used for running asynchronous timeout handlers that
have recently expired, and perhaps other things. The idea here is
that the QUIT macros occur extremely often in the code, but only occur
at places that are relatively safe – in particular, if an error occurs,
nothing will get completely trashed.
accept-process-output and
wait_delaying_user_events().) Formerly, this was supposed to
happen, but didn’t always due to a bizarre and broken scheme, documented
in next_event_internal like this:
If we read a C-g, then set
quit-flagbut do not discard the C-g. The callers ofnext_event_internal()will do one of two things:
- set
Vquit_flagto Qnil. (next-eventdoes this.) This will cause the ^G to be treated as a normal keystroke.- not change
Vquit_flagbut attempt to enqueue the ^G, at which point it will be discarded. The next time QUIT is called, it will notice thatVquit_flagwas set.
This required weirdness in enqueue_command_event_1 like this:
put the event on the typeahead queue, unless the event is the quit char, in which case the
QUITwhich will occur on the next trip through this loop is all the processing we should do - leaving it on the queue would cause the quit to be processed twice.
And further weirdness elsewhere, none of which made any sense, and
didn’t work, because (e.g.) it required that QUIT never happen anywhere
inside next_event_internal() or any callers when C-g should
be read as a user event, which was impossible to implement in practice.
Now what we do is fairly simple. Callers of
next_event_internal() that want C-g read as a user event
call begin_dont_check_for_quit(). next_event_internal(),
when it gets a C-g, simply sets Vquit_flag (just as when a
C-g is detected during the operation of QUIT or
QUITP), and then tries to QUIT. This will fail if blocked
by the previous call, at which point next_event_internal() will
return the C-g as an event. To unblock things, first set
Vquit_flag to nil (it was set to t when the C-g was read,
and if we don’t reset it, the next call to QUIT will quit), and
then unbind_to() the depth returned by
begin_dont_check_for_quit(). It makes no difference is
QUIT is called a zillion times in next_event_internal() or
anywhere else, because it’s blocked and will never signal.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Checking for QUIT can do quite a long of things – since it pumps the event loop, this may cause arbitrary code to get executed, garbage collection to happen. etc. (In fact, garbage collection cannot happen because it is inhibited.) This has led to crashes when functions get called reentrantly when not expecting it. Example:
re_match_2() /* dont_check_for_quit is set in three circumstances:
(1) when we are in the process of changing the window
configuration. The frame might be in an inconsistent state,
which will cause assertion failures if we check for QUIT.
(2) when we are reading events, and want to read the C-g
as an event. The normal check for quit will discard the C-g,
which would be bad.
(3) when we're going down with a fatal error. we're most likely
in an inconsistent state, and we definitely don't want to be
interrupted. */
/* We should *not* conditionalize on Vinhibit_quit, or
critical-quit (Control-Shift-G) won't work right. */
/* WARNING: Even calling check_quit(), without actually dispatching
a quit signal, can result in arbitrary Lisp code getting executed
-- at least under Windows. (Not to mention obvious Lisp
invocations like asynchronous timer callbacks.) Here's a sample
stack trace to demonstrate:
NTDLL! DbgBreakPoint@0 address 0x77f9eea9
assert_failed(const char * 0x012d036c, int 4596, const char * 0x012d0354) line 3478
re_match_2_internal(re_pattern_buffer * 0x012d6780, const unsigned char * 0x00000000, int 0, const unsigned char * 0x022f9328, int 34, int 0, re_registers * 0x012d53d0 search_regs, int 34) line 4596 + 41 bytes
re_search_2(re_pattern_buffer * 0x012d6780, const char * 0x00000000, int 0, const char * 0x022f9328, int 34, int 0, int 34, re_registers * 0x012d53d0 search_regs, int 34) line 4269 + 37 bytes
re_search(re_pattern_buffer * 0x012d6780, const char * 0x022f9328, int 34, int 0, int 34, re_registers * 0x012d53d0 search_regs) line 4031 + 37 bytes
string_match_1(long 31222628, long 30282164, long 28377092, buffer * 0x022fde00, int 0) line 413 + 69 bytes
Fstring_match(long 31222628, long 30282164, long 28377092, long 28377092) line 436 + 34 bytes
Ffuncall(int 3, long * 0x008297f8) line 3488 + 168 bytes
execute_optimized_program(const unsigned char * 0x020ddc50, int 6, long * 0x020ddf50) line 744 + 16 bytes
funcall_compiled_function(long 34407748, int 1, long * 0x00829aec) line 516 + 53 bytes
Ffuncall(int 2, long * 0x00829ae8) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x020ddc90, int 4, long * 0x020ddf90) line 744 + 16 bytes
funcall_compiled_function(long 34407720, int 1, long * 0x00829e28) line 516 + 53 bytes
Ffuncall(int 2, long * 0x00829e24) line 3523 + 17 bytes
mapcar1(long 15, long * 0x00829e48, long 34447820, long 34187868) line 2929 + 11 bytes
Fmapcar(long 34447820, long 34187868) line 3035 + 21 bytes
Ffuncall(int 3, long * 0x00829f20) line 3488 + 93 bytes
execute_optimized_program(const unsigned char * 0x020c2b70, int 7, long * 0x020dd010) line 744 + 16 bytes
funcall_compiled_function(long 34407580, int 2, long * 0x0082a210) line 516 + 53 bytes
Ffuncall(int 3, long * 0x0082a20c) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x020cf810, int 6, long * 0x020cfb10) line 744 + 16 bytes
funcall_compiled_function(long 34407524, int 0, long * 0x0082a580) line 516 + 53 bytes
Ffuncall(int 1, long * 0x0082a57c) line 3523 + 17 bytes
run_hook_with_args_in_buffer(buffer * 0x022fde00, int 1, long * 0x0082a57c, int 0) line 3980 + 13 bytes
run_hook_with_args(int 1, long * 0x0082a57c, int 0) line 3993 + 23 bytes
Frun_hooks(int 1, long * 0x0082a57c) line 3847 + 19 bytes
run_hook(long 34447484) line 4094 + 11 bytes
unsafe_handle_wm_initmenu_1(frame * 0x01dbb000) line 736 + 11 bytes
unsafe_handle_wm_initmenu(long 28377092) line 807 + 11 bytes
condition_case_1(long 28377116, long (long)* 0x0101c827 unsafe_handle_wm_initmenu(long), long 28377092, long (long, long)* 0x01005fa4 mswindows_modal_loop_error_handler(long, long), long 28377092) line 1692 + 7 bytes
mswindows_protect_modal_loop(long (long)* 0x0101c827 unsafe_handle_wm_initmenu(long), long 28377092) line 1194 + 32 bytes
mswindows_handle_wm_initmenu(HMENU__ * 0x00010199, frame * 0x01dbb000) line 826 + 17 bytes
mswindows_wnd_proc(HWND__ * 0x000501da, unsigned int 278, unsigned int 65945, long 0) line 3089 + 31 bytes
USER32! UserCallWinProc@20 + 24 bytes
USER32! DispatchClientMessage@20 + 47 bytes
USER32! __fnDWORD@4 + 34 bytes
NTDLL! KiUserCallbackDispatcher@12 + 19 bytes
USER32! DispatchClientMessage@20 address 0x77e163cc
USER32! DefWindowProcW@16 + 34 bytes
qxeDefWindowProc(HWND__ * 0x000501da, unsigned int 274, unsigned int 61696, long 98) line 1188 + 22 bytes
mswindows_wnd_proc(HWND__ * 0x000501da, unsigned int 274, unsigned int 61696, long 98) line 3362 + 21 bytes
USER32! UserCallWinProc@20 + 24 bytes
USER32! DispatchClientMessage@20 + 47 bytes
USER32! __fnDWORD@4 + 34 bytes
NTDLL! KiUserCallbackDispatcher@12 + 19 bytes
USER32! DispatchClientMessage@20 address 0x77e163cc
USER32! DefWindowProcW@16 + 34 bytes
qxeDefWindowProc(HWND__ * 0x000501da, unsigned int 262, unsigned int 98, long 540016641) line 1188 + 22 bytes
mswindows_wnd_proc(HWND__ * 0x000501da, unsigned int 262, unsigned int 98, long 540016641) line 3362 + 21 bytes
USER32! UserCallWinProc@20 + 24 bytes
USER32! DispatchMessageWorker@8 + 244 bytes
USER32! DispatchMessageW@4 + 11 bytes
qxeDispatchMessage(const tagMSG * 0x0082c684 {msg=0x00000106 wp=0x00000062 lp=0x20300001}) line 989 + 10 bytes
mswindows_drain_windows_queue() line 1345 + 9 bytes
emacs_mswindows_quit_p() line 3947
event_stream_quit_p() line 666
check_quit() line 686
check_what_happened() line 437
re_match_2_internal(re_pattern_buffer * 0x012d5a18, const unsigned char * 0x00000000, int 0, const unsigned char * 0x02235000, int 23486, int 14645, re_registers * 0x012d53d0 search_regs, int 23486) line 4717 + 14 bytes
re_search_2(re_pattern_buffer * 0x012d5a18, const char * 0x02235000, int 23486, const char * 0x0223b38e, int 0, int 14645, int 8841, re_registers * 0x012d53d0 search_regs, int 23486) line 4269 + 37 bytes
search_buffer(buffer * 0x022fde00, long 29077572, long 13789, long 23487, long 1, int 1, long 28377092, long 28377092, int 0) line 1224 + 89 bytes
search_command(long 29077572, long 46975, long 28377116, long 28377092, long 28377092, int 1, int 1, int 0) line 1054 + 151 bytes
Fre_search_forward(long 29077572, long 46975, long 28377116, long 28377092, long 28377092) line 2147 + 31 bytes
Ffuncall(int 4, long * 0x0082ceb0) line 3488 + 216 bytes
execute_optimized_program(const unsigned char * 0x02047810, int 13, long * 0x02080c10) line 744 + 16 bytes
funcall_compiled_function(long 34187208, int 3, long * 0x0082d1b8) line 516 + 53 bytes
Ffuncall(int 4, long * 0x0082d1b4) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x01e96a10, int 6, long * 0x020ae510) line 744 + 16 bytes
funcall_compiled_function(long 34186676, int 3, long * 0x0082d4a0) line 516 + 53 bytes
Ffuncall(int 4, long * 0x0082d49c) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x02156b50, int 4, long * 0x020c2db0) line 744 + 16 bytes
funcall_compiled_function(long 34186564, int 2, long * 0x0082d780) line 516 + 53 bytes
Ffuncall(int 3, long * 0x0082d77c) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x0082d964, int 3, long * 0x020c2d70) line 744 + 16 bytes
Fbyte_code(long 29405156, long 34352480, long 7) line 2392 + 38 bytes
Feval(long 34354440) line 3290 + 187 bytes
condition_case_1(long 34354572, long (long)* 0x01087232 Feval(long), long 34354440, long (long, long)* 0x01084764 run_condition_case_handlers(long, long), long 28377092) line 1692 + 7 bytes
condition_case_3(long 34354440, long 28377092, long 34354572) line 1779 + 27 bytes
execute_rare_opcode(long * 0x0082dc7c, const unsigned char * 0x01b090af, int 143) line 1269 + 19 bytes
execute_optimized_program(const unsigned char * 0x01b09090, int 6, long * 0x020ae590) line 654 + 17 bytes
funcall_compiled_function(long 34186620, int 0, long * 0x0082df68) line 516 + 53 bytes
Ffuncall(int 1, long * 0x0082df64) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x02195470, int 1, long * 0x020c2df0) line 744 + 16 bytes
funcall_compiled_function(long 34186508, int 0, long * 0x0082e23c) line 516 + 53 bytes
Ffuncall(int 1, long * 0x0082e238) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x01e5d410, int 6, long * 0x0207d410) line 744 + 16 bytes
funcall_compiled_function(long 34186312, int 1, long * 0x0082e524) line 516 + 53 bytes
Ffuncall(int 2, long * 0x0082e520) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x02108fb0, int 2, long * 0x020c2e30) line 744 + 16 bytes
funcall_compiled_function(long 34186340, int 0, long * 0x0082e7fc) line 516 + 53 bytes
Ffuncall(int 1, long * 0x0082e7f8) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x020fe150, int 2, long * 0x01e6f510) line 744 + 16 bytes
funcall_compiled_function(long 31008124, int 0, long * 0x0082ebd8) line 516 + 53 bytes
Ffuncall(int 1, long * 0x0082ebd4) line 3523 + 17 bytes
run_hook_with_args_in_buffer(buffer * 0x022fde00, int 1, long * 0x0082ebd4, int 0) line 3980 + 13 bytes
run_hook_with_args(int 1, long * 0x0082ebd4, int 0) line 3993 + 23 bytes
Frun_hooks(int 1, long * 0x0082ebd4) line 3847 + 19 bytes
Ffuncall(int 2, long * 0x0082ebd0) line 3509 + 14 bytes
execute_optimized_program(const unsigned char * 0x01ef2210, int 5, long * 0x01da8e10) line 744 + 16 bytes
funcall_compiled_function(long 31020440, int 2, long * 0x0082eeb8) line 516 + 53 bytes
Ffuncall(int 3, long * 0x0082eeb4) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x0082f09c, int 3, long * 0x01d89390) line 744 + 16 bytes
Fbyte_code(long 31102388, long 30970752, long 7) line 2392 + 38 bytes
Feval(long 31087568) line 3290 + 187 bytes
condition_case_1(long 30961240, long (long)* 0x01087232 Feval(long), long 31087568, long (long, long)* 0x01084764 run_condition_case_handlers(long, long), long 28510180) line 1692 + 7 bytes
condition_case_3(long 31087568, long 28510180, long 30961240) line 1779 + 27 bytes
execute_rare_opcode(long * 0x0082f450, const unsigned char * 0x01ef23ec, int 143) line 1269 + 19 bytes
execute_optimized_program(const unsigned char * 0x01ef2310, int 6, long * 0x01da8f10) line 654 + 17 bytes
funcall_compiled_function(long 31020412, int 1, long * 0x0082f740) line 516 + 53 bytes
Ffuncall(int 2, long * 0x0082f73c) line 3523 + 17 bytes
execute_optimized_program(const unsigned char * 0x020fe650, int 3, long * 0x01d8c490) line 744 + 16 bytes
funcall_compiled_function(long 31020020, int 2, long * 0x0082fa14) line 516 + 53 bytes
Ffuncall(int 3, long * 0x0082fa10) line 3523 + 17 bytes
Fcall_interactively(long 29685180, long 28377092, long 28377092) line 1008 + 22 bytes
Fcommand_execute(long 29685180, long 28377092, long 28377092) line 2929 + 17 bytes
execute_command_event(command_builder * 0x01be1900, long 36626492) line 4048 + 25 bytes
Fdispatch_event(long 36626492) line 4341 + 70 bytes
Fcommand_loop_1() line 582 + 9 bytes
command_loop_1(long 28377092) line 495
condition_case_1(long 28377188, long (long)* 0x01064fb9 command_loop_1(long), long 28377092, long (long, long)* 0x010649d0 cmd_error(long, long), long 28377092) line 1692 + 7 bytes
command_loop_3() line 256 + 35 bytes
command_loop_2(long 28377092) line 269
internal_catch(long 28457612, long (long)* 0x01064b20 command_loop_2(long), long 28377092, int * volatile 0x00000000) line 1317 + 7 bytes
initial_command_loop(long 28377092) line 305 + 25 bytes
STACK_TRACE_EYE_CATCHER(int 1, char * * 0x01b63ff0, char * * 0x01ca5300, int 0) line 2501
main(int 1, char * * 0x01b63ff0, char * * 0x01ca5300) line 2938
XEMACS! mainCRTStartup + 180 bytes
_start() line 171
KERNEL32! BaseProcessStart@4 + 115547 bytes
|
[explain dont_check_for_quit() et al]
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
We implement our own profiling scheme so that we can determine things like which Lisp functions are occupying the most time. Any standard OS-provided profiling works on C functions, which is not always that useful – and inconvenient, since it requires compiling with profile info and can’t be retrieved dynamically, as XEmacs is running.
The basic idea is simple. We set a profiling timer using setitimer
(ITIMER_PROF), which generates a SIGPROF every so often. (This runs not
in real time but rather when the process is executing or the system is
running on behalf of the process – at least, that is the case under
Unix. Under MS Windows and Cygwin, there is no setitimer(), so we
simulate it using multimedia timers, which run in real time. To make
the results a bit more realistic, we ignore ticks that go off while
blocking on an event wait. Note that Cygwin does provide a simulation
of setitimer(), but it’s in real time anyway, since Windows doesn’t
provide a way to have process-time timers, and furthermore, it’s broken,
so we don’t use it.) When the signal goes off, we see what we’re in, and
add 1 to the count associated with that function.
It would be nice to use the Lisp allocation mechanism etc. to keep track
of the profiling information (i.e. to use Lisp hash tables), but we
can’t because that’s not safe – updating the timing information happens
inside of a signal handler, so we can’t rely on not being in the middle
of Lisp allocation, garbage collection, malloc(), etc. Trying to make
it work would be much more work than it’s worth. Instead we use a basic
(non-Lisp) hash table, which will not conflict with garbage collection
or anything else as long as it doesn’t try to resize itself. Resizing
itself, however (which happens as a result of a puthash()), could be
deadly. To avoid this, we make sure, at points where it’s safe
(e.g. profile_record_about_to_call() – recording the entry into a
function call), that the table always has some breathing room in it so
that no resizes will occur until at least that many items are added.
This is safe because any new item to be added in the sigprof would
likely have the profile_record_about_to_call() called just before it,
and the breathing room is checked.
In general: any entry that the sigprof handler puts into the table comes
from a backtrace frame (except "Processing Events at Top Level", and
there’s only one of those). Either that backtrace frame was added when
profiling was on (in which case profile_record_about_to_call() was
called and the breathing space updated), or when it was off – and in
this case, no such frames can have been added since the last time
start-profile was called, so when start-profile is called we make
sure there is sufficient breathing room to account for all entries
currently on the stack.
Jan 1998: In addition to timing info, I have added code to remember call
counts of Lisp funcalls. The profile_increase_call_count()
function is called from Ffuncall(), and serves to add data to
Vcall_count_profile_table. This mechanism is much simpler and
independent of the SIGPROF-driven one. It uses the Lisp allocation
mechanism normally, since it is not called from a handler. It may
even be useful to provide a way to turn on only one profiling
mechanism, but I haven’t done so yet. –hniksic
Dec 2002: Total overhaul of the interface, making it sane and easier to use. –ben
Feb 2003: Lots of rewriting of the internal code. Add GC-consing-usage, total GC usage, and total timing to the information tracked. Track profiling overhead and allow the ability to have internal sections (e.g. internal-external conversion, byte-char conversion) that are treated like Lisp functions for the purpose of profiling. –ben
BEWARE: If you are modifying this file, be very careful. Correctly implementing the "total" values is very tricky due to the possibility of recursion and of functions already on the stack when starting to profile/still on the stack when stopping.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Ben’s capsule summary about expected and unexpected exits from XEmacs.
Expected exits occur when the user directs XEmacs to exit, for example
by pressing the close button on the only frame in XEmacs, or by typing
C-x C-c. This runs save-buffers-kill-emacs, which saves
any necessary buffers, and then exits using the primitive
kill-emacs.
However, unexpected exits occur in a few different ways:
Currently, both unexpected exit scenarios described above set
preparing_for_armageddon to indicate that nonessential and possibly
dangerous things should not be done, specifically:
(Also, all places that set preparing_for_armageddon also
set dont_check_for_quit. This happens separately because it’s
also necessary to set other variables to make absolutely sure
no quitting happens.)
In the first scenario above (the access violation), we also set
fatal_error_in_progress. This causes more things to not happen:
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Aidan Kehoe on December 27, 2016 using texi2html 1.82.