comparison liboctave/util/cmd-edit.cc @ 24520:c5c11b07598a

refactor signal handling (bug #52757) Given that any library code may now be multi-threaded it is not likely to be save to jump out of a signal handler when executing a call to a library function. We are almost assured to fail if the call to siglongjmp occurs in a different thread from the corresponding call to sigsetjmp. Similarly, we should avoid calling the interrupt signal handler function in an arbitrary thread. This is already done by default on Windows systems. On Posix systems, we now set up a separate thread to handle asynchronous signals (not just SIGINT). We no longer attempt to jump out of the signal handler. Instead, we only set global status variables and respond to them in octave_quit. This means that some external library functions are no longer interruptible. If that becomes important, then we will need to look for other alternatives such as setting up worker threads that can be canceled when a SIGINT is detected. * sighandlers.cc (interrupt_manager, base_interrupt_manager, w32_interrupt_manager, posix_interrupt_manager): Delete classes and all uses. (respond_to_pending_signals): Rename from signal_handler. Change all uses. Take action on more signals. (generic_sig_handler): Simply record that a signal was caught and add it to the list of caught signals. (deadly_sig_handler): New function. (fpe_sig_handler): Rename from sigfpe_handler. Don't make specific to Alpha systems. Simply warn that an exception has occurred. (sigint_handler): Simply record that an interrupt signal was caught. (sigterm_handler, sigpipe_handler): Delete. (install_signal_handlers): Rework actions for various signals. (Vsigquit_dumps_octave_core): New static variable. (Fsigquit_dumps_octave_core): New function. * io.txi: Document sigquit_dumps_octave_core. * f77-fcn.h, f77-fcn.c (xSTRINGIZE, STRINGIZE, F77_XFCN_ERROR): Delete macros. (F77_XFCN): Don't do setjmp/longjmp exception handling. (f77_exception_encountered): Delete global variable. (XSTOPX): Don't set f77_exception_encountered. If message is empty, report "unknown error in fortran subroutine". * eigs-base.cc, gsvd.cc: Delete all direct uses of f77_exception_encountered. * liboctave/util/f77-extern.cc: Delete file. * liboctave/util/module.mk: Update. * cmd-edit.h, cmd-edit.cc (command_editor::interrupt_exception): New class. (command_editor::handle_interrupt_signal): New static function. (command_editor::do_handle_interrupt_signal): New virtual function. (command_editor::event_hanlder): Call handle_interrupt_signal if octave_interrupt_state is not zero. (command_editor::instance_ok): Set event hook here. (command_editor::add_event_hook): Not here. (command_editor::remove_event_hook): Never remove event hook. (gnu_readline::do_handle_interrupt_signal): New function. (gnu_readline::do_readline): Retry if an interrupt exception occurs inside readline. * oct-rl-edit.h, oct-rl-edit.c (octave_rl_recover_from_interrupt): New function. * quit.h, cquit.c (current_context, octave_save_current_context, octave_restore_current_context, octave_jump_to_enclosing_context, octave_interrupt_immediately, octave_jmp_buf): Delete. * quit.h: Don't include <setjmp.h> (octave_set_current_context): Delete macro. (octave_exit_exception_status, octave_exit_exception_safe_to_return): Mark as deprecated. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1): Delete. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2): Delete. (BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE, END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE): Define to only create a dummy do-while scope. (BEGIN_INTERRUPT_WITH_EXCEPTIONS, END_INTERRUPT_WITH_EXCEPTIONS): Define to only create a dummy scope. * interpreter.cc (interpreter::recover_from_exception): Don't reset octave_interrupt_immediately. * main-cli.cc (main): Call octave_block_async_signals. * main.in.cc (main): Call octave_block_async_signals initially. Only in install signal handlers and unblock signals if starting managed subprocess. * main-window.cc (octave_interpreter::executed): Don't unblock interrupt signals. * signal-wrappers.h, signal-wrappers.c (signal_watcher, octave_async_signals, block_or_unblock_signal_by_name): New static functions. (octave_set_default_signal_handler, octave_set_default_signal_handler_by_name, octave_block_signal_by_name, octave_unblock_signal_by_name, octave_create_interrupt_watcher_thread, octave_block_async_signals, octave_unblock_async_signals): New functions. (octave_block_interrupt_signal, octave_unblock_interrupt_signal): Treat SIGBREAK as an interrupt signal. (octave_block_child): Also handle SIGCLD the same as SIGCHLD. (print_sigset, print_sigmask): New static functions. (octave_show_sigmask): New function.
author John W. Eaton <jwe@octave.org>
date Wed, 03 Jan 2018 07:52:11 -0500
parents 061a343089be
children 194eb4bd202b
comparison
equal deleted inserted replaced
24519:6c31b0376908 24520:c5c11b07598a
193 193
194 bool do_prefer_env_winsize (bool); 194 bool do_prefer_env_winsize (bool);
195 195
196 void do_interrupt (bool); 196 void do_interrupt (bool);
197 197
198 void do_handle_interrupt_signal (void);
199
198 static int operate_and_get_next (int, int); 200 static int operate_and_get_next (int, int);
199 201
200 static int history_search_backward (int, int); 202 static int history_search_backward (int, int);
201 203
202 static int history_search_forward (int, int); 204 static int history_search_forward (int, int);
285 287
286 eof = false; 288 eof = false;
287 289
288 const char *p = prompt.c_str (); 290 const char *p = prompt.c_str ();
289 291
290 BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; 292 while (true)
291 293 {
292 char *line = ::octave_rl_readline (p); 294 try
293 295 {
294 if (line) 296 char *line = ::octave_rl_readline (p);
295 { 297
296 retval = line; 298 if (line)
297 299 {
298 free (line); 300 retval = line;
299 } 301
300 else 302 free (line);
301 eof = true; 303 }
302 304 else
303 END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; 305 eof = true;
306
307 break;
308 }
309 catch (command_editor::interrupt_exception&)
310 {
311 // Is this right?
312 std::cout << "\n";
313
314 // Try again...
315 }
316 }
304 317
305 return retval; 318 return retval;
306 } 319 }
307 320
308 void 321 void
780 gnu_readline::do_interrupt (bool arg) 793 gnu_readline::do_interrupt (bool arg)
781 { 794 {
782 ::octave_rl_done (arg); 795 ::octave_rl_done (arg);
783 } 796 }
784 797
798 void
799 gnu_readline::do_handle_interrupt_signal (void)
800 {
801 octave_signal_caught = 0;
802 octave_interrupt_state = 0;
803
804 ::octave_rl_recover_from_interrupt ();
805
806 throw command_editor::interrupt_exception ();
807 }
808
785 int 809 int
786 gnu_readline::operate_and_get_next (int /* count */, int /* c */) 810 gnu_readline::operate_and_get_next (int /* count */, int /* c */)
787 { 811 {
788 // Accept the current line. 812 // Accept the current line.
789 813
1062 if (! instance) 1086 if (! instance)
1063 { 1087 {
1064 make_command_editor (); 1088 make_command_editor ();
1065 1089
1066 if (instance) 1090 if (instance)
1067 singleton_cleanup_list::add (cleanup_instance); 1091 {
1092 instance->set_event_hook (event_handler);
1093
1094 singleton_cleanup_list::add (cleanup_instance);
1095 }
1068 } 1096 }
1069 1097
1070 if (! instance) 1098 if (! instance)
1071 (*current_liboctave_error_handler) 1099 (*current_liboctave_error_handler)
1072 ("unable to create command history object!"); 1100 ("unable to create command history object!");
1129 } 1157 }
1130 1158
1131 int 1159 int
1132 command_editor::event_handler (void) 1160 command_editor::event_handler (void)
1133 { 1161 {
1162 if (octave_interrupt_state)
1163 handle_interrupt_signal ();
1164
1134 event_hook_lock.lock (); 1165 event_hook_lock.lock ();
1135 1166
1136 std::set<event_hook_fcn> hook_set (event_hook_set); 1167 std::set<event_hook_fcn> hook_set (event_hook_set);
1137 1168
1138 event_hook_lock.unlock (); 1169 event_hook_lock.unlock ();
1537 void 1568 void
1538 command_editor::add_event_hook (event_hook_fcn f) 1569 command_editor::add_event_hook (event_hook_fcn f)
1539 { 1570 {
1540 autolock guard (event_hook_lock); 1571 autolock guard (event_hook_lock);
1541 1572
1542 if (instance_ok ()) 1573 event_hook_set.insert (f);
1543 {
1544 event_hook_set.insert (f);
1545
1546 instance->set_event_hook (event_handler);
1547 }
1548 } 1574 }
1549 1575
1550 void 1576 void
1551 command_editor::remove_event_hook (event_hook_fcn f) 1577 command_editor::remove_event_hook (event_hook_fcn f)
1552 { 1578 {
1553 autolock guard (event_hook_lock); 1579 autolock guard (event_hook_lock);
1554 1580
1555 if (instance_ok ()) 1581 auto p = event_hook_set.find (f);
1556 { 1582
1557 auto p = event_hook_set.find (f); 1583 if (p != event_hook_set.end ())
1558 1584 event_hook_set.erase (p);
1559 if (p != event_hook_set.end ()) 1585
1560 event_hook_set.erase (p);
1561
1562 if (event_hook_set.empty ())
1563 instance->restore_event_hook ();
1564 }
1565 } 1586 }
1566 1587
1567 void 1588 void
1568 command_editor::run_event_hooks (void) 1589 command_editor::run_event_hooks (void)
1569 { 1590 {
1625 } 1646 }
1626 else 1647 else
1627 retval = false; 1648 retval = false;
1628 1649
1629 return retval; 1650 return retval;
1651 }
1652
1653 void
1654 command_editor::handle_interrupt_signal (void)
1655 {
1656 if (instance_ok ())
1657 instance->do_handle_interrupt_signal ();
1630 } 1658 }
1631 1659
1632 // Return a string which will be printed as a prompt. The string may 1660 // Return a string which will be printed as a prompt. The string may
1633 // contain special characters which are decoded as follows: 1661 // contain special characters which are decoded as follows:
1634 // 1662 //