[ < ] | [ > ] | [ << ] | [ 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-flag
but do not discard the C-g. The callers ofnext_event_internal()
will do one of two things:
- set
Vquit_flag
to Qnil. (next-event
does this.) This will cause the ^G to be treated as a normal keystroke.- not change
Vquit_flag
but attempt to enqueue the ^G, at which point it will be discarded. The next time QUIT is called, it will notice thatVquit_flag
was 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
QUIT
which 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.