# HG changeset patch # User jwe # Date 1108175375 0 # Node ID 0f9108f298ab6a416820f1a673e9926b7028abd4 # Parent f51d2e9681f4a89f169a12946876c3adde8246af [project @ 2005-02-12 02:29:34 by jwe] diff --git a/libcruft/ChangeLog b/libcruft/ChangeLog --- a/libcruft/ChangeLog +++ b/libcruft/ChangeLog @@ -1,3 +1,13 @@ +2005-02-10 John W. Eaton + + * misc/cquit.c (octave_signal_caught): New global variable. + * misc/quit.cc (occtave_handle_signal): New function. + (octave_signal_hook): New global pointer. + * misc/quit.h: Provide decls. + (OCTAVE_QUIT): Check octave_signal_caught, not + octave_interrupt_state, and call octave_handle_signal, not + octave_throw_interrupt_exception. + 2005-02-08 John W. Eaton * misc/quit.h: Use C-style comments. diff --git a/libcruft/misc/cquit.c b/libcruft/misc/cquit.c --- a/libcruft/misc/cquit.c +++ b/libcruft/misc/cquit.c @@ -87,6 +87,8 @@ sig_atomic_t octave_allocation_error = 0; +sig_atomic_t octave_signal_caught = 0; + /* ;;; Local Variables: *** ;;; mode: C++ *** diff --git a/libcruft/misc/quit.cc b/libcruft/misc/quit.cc --- a/libcruft/misc/quit.cc +++ b/libcruft/misc/quit.cc @@ -26,14 +26,29 @@ #include +#include #include #include "quit.h" +void (*octave_signal_hook) (void) = 0; void (*octave_interrupt_hook) (void) = 0; void (*octave_bad_alloc_hook) (void) = 0; void +octave_handle_signal (void) +{ + if (octave_signal_hook) + octave_signal_hook (); + + if (octave_interrupt_state > 0) + { + octave_interrupt_state = -1; + octave_throw_interrupt_exception (); + } +} + +void octave_throw_interrupt_exception (void) { if (octave_interrupt_hook) diff --git a/libcruft/misc/quit.h b/libcruft/misc/quit.h --- a/libcruft/misc/quit.h +++ b/libcruft/misc/quit.h @@ -77,6 +77,10 @@ extern sig_atomic_t octave_allocation_error; +extern sig_atomic_t octave_signal_caught; + +extern void octave_handle_signal (void); + extern void octave_throw_interrupt_exception (void) GCC_ATTR_NORETURN; extern void octave_throw_bad_alloc (void) GCC_ATTR_NORETURN; @@ -84,10 +88,10 @@ #define OCTAVE_QUIT \ do \ { \ - if (octave_interrupt_state > 0) \ + if (octave_signal_caught) \ { \ - octave_interrupt_state = -1; \ - octave_throw_interrupt_exception (); \ + octave_signal_caught = 0; \ + octave_handle_signal (); \ } \ } \ while (0) @@ -169,6 +173,7 @@ /* These should only be declared for C++ code, and should also be outside of any extern "C" block. */ +extern void (*octave_signal_hook) (void); extern void (*octave_interrupt_hook) (void); extern void (*octave_bad_alloc_hook) (void); diff --git a/src/ChangeLog b/src/ChangeLog --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,60 @@ +2005-02-11 John W. Eaton + + * sighandlers.cc (sigpipe_handler): Don't try to take action. + Set octave_signal_caught and octave_signals_caught here. + (sigchld_handler): Call octave_child_list::wait here. + Set octave_signal_caught and octave_signals_caught here. + (octave_signals_caught): New static file-scope variable. + (sigint_handler): Also set octave_signal_caught. + + * sighandlers.cc (sigpipe_handler): + + * DLD-FUNCTIONS/gplot.l (plot_stream_event_handler): Rename from + plot_stream_death_handler. Return true if plotter has exited. + Call close_plot_stream with false arg. + (plot_stream_pid): Delete static file-scope variable and all uses. + (close_plot_stream): New arg, remove_from_child_list, with default + value of true. Only call octave_child_list::remove if + remove_from_child_list is true. + + * pager.cc (octave_pager_pid, saved_interrupt_handler, + interrupt_handler_saved): Delete static file-scope variables and + all uses. + (clear_external_pager): Do nothing if external_pager is 0. + (pager_event_handler): Rename from pager_death_handler. + Improve warning message. Return true if pager has exited. + (octave_pager_buf::do_sync): Check errno == EPIPE after write. + (flush_octave_stdout): No need to check external_pager before + calling clear_external_pager. + + * toplev.cc (recover_from_exception): Set octave_signal_caught to zero. + (main_loop): Set octave_signal_hook to octave_signal_handler here. + + * sighandlers.cc (octave_signal_handler): New function. + * sighanlders.h: Provide decl. + + * sighandlers.cc (install_signal_handlers): Initialize + octave_signals_caught. + + * sighandlers.h (octave_child::status, have_status): New data members. + (child_event_handler): Rename from dead_child_handler. + Change all uses. + (octave_child_list::reap, octave_child_list::wait): New functions. + (octave_child_list::length, octave_child_list::do_length, + octave_child_list::elem, octave_child_list::do_elem, + octave_child_list::list, octave_child_list::curr_len): Delete. + + * sighandlers.h, sighandlers.cc + (octave_child_list::octave_child_list_rep): New class. + + * src/input.cc (gnu_readline, octave_gets, get_user_input): + Call OCTAVE_QUIT before doing anything. + + * base-list.h (octave_base_list::remove_if): New function. + + * TEMPLATE-INST/Array-oc.cc: Delete. + * Makefile.in (TI_XSRC): Remove from list. + 2005-02-10 Driss Ghaddab * cutils.c (octave_usleep) [HAVE_POLL]: Fix typo. diff --git a/src/DLD-FUNCTIONS/gplot.l b/src/DLD-FUNCTIONS/gplot.l --- a/src/DLD-FUNCTIONS/gplot.l +++ b/src/DLD-FUNCTIONS/gplot.l @@ -257,9 +257,6 @@ // Pipe to gnuplot. static oprocstream *plot_stream = 0; -// ID of the plotter process. -static pid_t plot_stream_pid = 0; - // Gnuplot command strings that we use. static std::string Vgnuplot_command_plot; static std::string Vgnuplot_command_replot; @@ -309,13 +306,15 @@ } static void -close_plot_stream (void) +close_plot_stream (bool remove_from_child_list = true) { - octave_child_list::remove (plot_stream_pid); - if (plot_stream) { + if (remove_from_child_list) + octave_child_list::remove (plot_stream->pid ()); + send_to_plot_stream ("\nquit\n"); + delete plot_stream; plot_stream = 0; } @@ -323,13 +322,28 @@ plot_line_count = 0; } -static void -plot_stream_death_handler (pid_t pid, int) +static bool +plot_stream_event_handler (pid_t pid, int status) { - close_plot_stream (); + bool retval = false; + + if (pid > 0) + { + if (WIFEXITED (status) || WIFSIGNALLED (status)) + { + close_plot_stream (false); - warning ("connection to external plotter (pid = %d) lost --", pid); - warning ("please try your plot command(s) again"); + warning ("connection to external plotter (pid = %d) lost --", pid); + warning ("please try your plot command(s) again"); + + // Request removal of this PID from the list of child + // processes. + + retval = true; + } + } + + return retval; } static void @@ -381,11 +395,8 @@ error ("plot: unable to open pipe to `%s'", plot_prog.c_str ()); } else - { - plot_stream_pid = plot_stream->pid (); - octave_child_list::insert (plot_stream_pid, - plot_stream_death_handler); - } + octave_child_list::insert (plot_stream->pid (), + plot_stream_event_handler); } else error ("plot: unable to open pipe to `%s'", plot_prog.c_str ()); diff --git a/src/Makefile.in b/src/Makefile.in --- a/src/Makefile.in +++ b/src/Makefile.in @@ -97,7 +97,7 @@ symtab.h sysdep.h token.h toplev.h unwind-prot.h utils.h \ variables.h version.h xdiv.h xpow.h $(OV_INCLUDES) $(PT_INCLUDES) -TI_XSRC := Array-oc.cc Array-os.cc Array-sym.cc Array-tc.cc +TI_XSRC := Array-os.cc Array-sym.cc Array-tc.cc TI_SRC := $(addprefix TEMPLATE-INST/, $(TI_XSRC)) diff --git a/src/TEMPLATE-INST/Array-oc.cc b/src/TEMPLATE-INST/Array-oc.cc deleted file mode 100644 --- a/src/TEMPLATE-INST/Array-oc.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - -Copyright (C) 1996, 1997 John W. Eaton - -This file is part of Octave. - -Octave is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -Octave is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Octave; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -// Instantiate Arrays of octave_child objects. - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "Array.h" -#include "Array.cc" - -#include "sighandlers.h" - -INSTANTIATE_ARRAY (octave_child); - -/* -;;; Local Variables: *** -;;; mode: C++ *** -;;; End: *** -*/ diff --git a/src/base-list.h b/src/base-list.h --- a/src/base-list.h +++ b/src/base-list.h @@ -40,6 +40,9 @@ iterator erase (iterator pos) { return lst.erase (pos); } + template + void remove_if (P pred) { lst.remove_if (pred); } + void clear (void) { lst.clear (); } void append (const elt_type& s) { lst.push_back (s); } diff --git a/src/input.cc b/src/input.cc --- a/src/input.cc +++ b/src/input.cc @@ -165,6 +165,8 @@ std::string gnu_readline (const std::string& s, bool force_readline) { + OCTAVE_QUIT; + std::string retval; if (line_editing || force_readline) @@ -204,6 +206,8 @@ static std::string octave_gets (void) { + OCTAVE_QUIT; + std::string retval; Vlast_prompt_time.stamp (); @@ -254,6 +258,8 @@ static std::string get_user_input (void) { + OCTAVE_QUIT; + std::string retval; if (get_input_from_eval_string) diff --git a/src/pager.cc b/src/pager.cc --- a/src/pager.cc +++ b/src/pager.cc @@ -44,8 +44,6 @@ #include "utils.h" #include "variables.h" -static pid_t octave_pager_pid = -1; - // Our actual connection to the external pager. static oprocstream *external_pager = 0; @@ -70,11 +68,6 @@ // through the pager. static bool Vpage_screen_output; -// Only one pager can be active at once, so having these at file -// scope should be ok. -static octave_interrupt_handler saved_interrupt_handler; -static bool interrupt_handler_saved = false; - static bool really_flush_to_pager = false; static bool flushing_output_to_pager = false; @@ -82,35 +75,40 @@ static void clear_external_pager (void) { - octave_child_list::remove (octave_pager_pid); - - octave_pager_pid = -1; + if (external_pager) + { + octave_child_list::remove (external_pager->pid ()); - delete external_pager; - external_pager = 0; - - if (interrupt_handler_saved) - { - octave_set_interrupt_handler (saved_interrupt_handler); - interrupt_handler_saved = false; + delete external_pager; + external_pager = 0; } } -static void -pager_death_handler (pid_t pid, int status) +static bool +pager_event_handler (pid_t pid, int status) { + bool retval = false; + if (pid > 0) { if (WIFEXITED (status) || WIFSIGNALLED (status)) { - // Avoid warning() or error(), since that will put us back in - // the pager, which would be bad news. + // Avoid warning() since that will put us back in the pager, + // which would be bad news. - std::cerr << "warning: connection to external pager (pid = " - << pid << ") lost --\n" - << "warning: attempting to finish pending computations...\n"; + std::cerr << "warning: connection to external pager lost (pid = " + << pid << ")" << std::endl; + std::cerr << "warning: flushing pending output (please wait)" + << std::endl; + + // Request removal of this PID from the list of child + // processes. + + retval = true; } } + + return retval; } static void @@ -131,47 +129,33 @@ if (! pgr.empty ()) { - saved_interrupt_handler = octave_ignore_interrupts (); - interrupt_handler_saved = true; - external_pager = new oprocstream (pgr.c_str ()); if (external_pager) - { - octave_pager_pid = external_pager->pid (); - - octave_child_list::insert (octave_pager_pid, - pager_death_handler); - } + octave_child_list::insert (external_pager->pid (), + pager_event_handler); } } if (external_pager) { - if (octave_pager_pid > 0 && external_pager->good ()) + if (external_pager->good ()) { external_pager->write (msg, len); - // These checks are needed if a signal handler - // invoked since the last set of checks attempts - // to flush output and then returns + external_pager->flush (); - if (octave_pager_pid > 0 - && external_pager - && external_pager->good ()) - external_pager->flush (); +#if defined (EPIPE) + if (errno == EPIPE) + external_pager->setstate (std::ios::failbit); +#endif } else { - // We had a pager, but it must have died. Restore - // the interrupt state so we can escape back to the - // prompt if there are lots of computations pending. - - if (interrupt_handler_saved) - { - octave_set_interrupt_handler (saved_interrupt_handler); - interrupt_handler_saved = false; - } + // XXX FIXME XXX -- omething is not right with the + // pager. If it died then we should receive a + // signal for that. If there is some other problem, + // then what? } } else @@ -361,8 +345,7 @@ octave_stdout.flush (); - if (external_pager) - clear_external_pager (); + clear_external_pager (); unwind_protect::run_frame ("flush_octave_stdout"); } diff --git a/src/sighandlers.cc b/src/sighandlers.cc --- a/src/sighandlers.cc +++ b/src/sighandlers.cc @@ -93,6 +93,42 @@ do { } while (0) #endif +// List of signals we have caught since last call to octave_signal_handler. +static bool octave_signals_caught[NSIG]; + +// Called from OCTAVE_QUIT to actually do something about the signals +// we have caught. + +void +octave_signal_handler (void) +{ + // The list of signals is relatively short, so we will just go + // linearly through the list. + + for (int i = 0; i < NSIG; i++) + { + if (octave_signals_caught[i]) + { + octave_signals_caught[i] = false; + + switch (i) + { + case SIGCHLD: + octave_child_list::reap (); + break; + + case SIGFPE: + std::cerr << "warning: floating point exception -- trying to return to prompt" << std::endl; + break; + + case SIGPIPE: + std::cerr << "warning: broken pipe -- some output may be lost" << std::endl; + break; + } + } + } +} + static void my_friendly_exit (const char *sig_name, int sig_number, bool save_vars = true) @@ -181,30 +217,13 @@ BLOCK_CHILD (set, oset); - int n = octave_child_list::length (); - - for (int i = 0; i < n; i++) + if (octave_child_list::wait ()) { - octave_child& elt = octave_child_list::elem (i); - - pid_t pid = elt.pid; - - if (pid > 0) - { - int status; + // The status of some child changed. - if (waitpid (pid, &status, WNOHANG) > 0) - { - elt.pid = -1; - - octave_child::dead_child_handler f = elt.handler; + octave_signal_caught = 1; - if (f) - f (pid, status); - - break; - } - } + octave_signals_caught[SIGCHLD] = true; } octave_set_interrupt_handler (saved_interrupt_handler); @@ -232,13 +251,14 @@ MAYBE_REINSTALL_SIGHANDLER (SIGFPE, sigfpe_handler); - std::cerr << "error: floating point exception -- trying to return to prompt\n"; + if (can_interrupt && octave_interrupt_state >= 0) + { + octave_signal_caught = 1; - // XXX FIXME XXX -- will setting octave_interrupt_state really help - // here? + octave_signals_caught[SIGFPE] = true; - if (can_interrupt && octave_interrupt_state >= 0) - octave_interrupt_state++; + octave_interrupt_state++; + } SIGHANDLER_RETURN (0); } @@ -341,6 +361,7 @@ if (octave_interrupt_state < 0) octave_interrupt_state = 0; + octave_signal_caught = 1; octave_interrupt_state++; if (interactive && octave_interrupt_state == 2) @@ -362,15 +383,13 @@ MAYBE_REINSTALL_SIGHANDLER (SIGPIPE, sigpipe_handler); - if (pipe_handler_error_count++ == 0) - std::cerr << "warning: broken pipe\n"; + octave_signal_caught = 1; + + octave_signals_caught[SIGPIPE] = true; // Don't loop forever on account of this. - // XXX FIXME XXX -- will setting octave_interrupt_state really help - // here? - - if (pipe_handler_error_count > 100 && octave_interrupt_state >= 0) + if (pipe_handler_error_count++ > 100 && octave_interrupt_state >= 0) octave_interrupt_state++; SIGHANDLER_RETURN (0); @@ -430,6 +449,9 @@ void install_signal_handlers (void) { + for (int i = 0; i < NSIG; i++) + octave_signals_caught[i] = false; + octave_catch_interrupts (); #ifdef SIGABRT @@ -702,13 +724,7 @@ return m; } -octave_child_list *octave_child_list::instance = 0; - -// This needs to be here for linking on AIX, at least for some -// versions of GCC, otherwise we fail with unresolved references to -// the Array destructor. - -octave_child_list::~octave_child_list (void) { } +octave_child_list::octave_child_list_rep *octave_child_list::instance = 0; bool octave_child_list::instance_ok (void) @@ -716,7 +732,7 @@ bool retval = true; if (! instance) - instance = new octave_child_list (); + instance = new octave_child_list_rep (); if (! instance) { @@ -729,104 +745,112 @@ } void -octave_child_list::insert (pid_t pid, octave_child::dead_child_handler f) +octave_child_list::insert (pid_t pid, octave_child::child_event_handler f) +{ + if (instance_ok ()) + instance->insert (pid, f); +} + +void +octave_child_list::reap (void) { if (instance_ok ()) - instance->do_insert (pid, f); + instance->reap (); +} + +bool +octave_child_list::wait (void) +{ + return (instance_ok ()) ? instance->wait () : false; } +class pid_equal +{ +public: + + pid_equal (pid_t v) : val (v) { } + + bool operator () (const octave_child& oc) const { return oc.pid == val; } + +private: + + pid_t val; +}; + void octave_child_list::remove (pid_t pid) { if (instance_ok ()) - instance->do_remove (pid); + instance->remove_if (pid_equal (pid)); } -int -octave_child_list::length (void) -{ - return (instance_ok ()) ? instance->do_length () : 0; -} +#define OCL_REP octave_child_list::octave_child_list_rep -octave_child& -octave_child_list::elem (int i) +void +OCL_REP::insert (pid_t pid, octave_child::child_event_handler f) { - static octave_child foo; - - return (instance_ok ()) ? instance->do_elem (i) : foo; + append (octave_child (pid, f)); } void -octave_child_list::do_insert (pid_t pid, octave_child::dead_child_handler f) +OCL_REP::reap (void) { - // Insert item in first open slot, increasing size of list if - // necessary. + // Mark the record for PID invalid. - bool enlarge = true; + for (iterator p = begin (); p != end (); p++) + { + // The call to the octave_child::child_event_handler might + // invalidate the iterator (for example, by calling + // octave_child_list::remove), so we increment the iterator + // here. - for (int i = 0; i < curr_len; i++) - { - octave_child& tmp = list (i); + octave_child& oc = *p; - if (tmp.pid < 0) + if (oc.have_status) { - list (i) = octave_child (pid, f); - enlarge = false; - break; + oc.have_status = 0; + + octave_child::child_event_handler f = oc.handler; + + if (f && f (oc.pid, oc.status)) + oc.pid = -1; } } - if (enlarge) - { - int total_len = list.length (); - - if (curr_len == total_len) - { - if (total_len == 0) - list.resize (16); - else - list.resize (total_len * 2); - } - - list (curr_len) = octave_child (pid, f); - curr_len++; - } + remove_if (pid_equal (-1)); } -void -octave_child_list::do_remove (pid_t pid) +// Wait on our children and record any changes in their status. + +bool +OCL_REP::wait (void) { - // Mark the record for PID invalid. + bool retval = false; - for (int i = 0; i < curr_len; i++) + for (iterator p = begin (); p != end (); p++) { - octave_child& tmp = list (i); + octave_child& oc = *p; + + pid_t pid = oc.pid; + + if (pid > 0) + { + int status; - if (tmp.pid == pid) - { - tmp.pid = -1; - break; + if (waitpid (pid, &status, WNOHANG) > 0) + { + oc.have_status = 1; + + oc.status = status; + + retval = true; + + break; + } } } -} -int -octave_child_list::do_length (void) const -{ - return curr_len; -} - -octave_child& -octave_child_list::do_elem (int i) -{ - static octave_child foo; - - int n = do_length (); - - if (i >= 0 && i < n) - return list (i); - else - return foo; + return retval; } static int diff --git a/src/sighandlers.h b/src/sighandlers.h --- a/src/sighandlers.h +++ b/src/sighandlers.h @@ -41,7 +41,7 @@ #include "syswait.h" #include "siglist.h" -#include +#include "base-list.h" // Signal handler return type. #ifndef RETSIGTYPE @@ -96,6 +96,8 @@ extern void install_signal_handlers (void); +extern void octave_signal_handler (void); + extern octave_interrupt_handler octave_catch_interrupts (void); extern octave_interrupt_handler octave_ignore_interrupts (void); @@ -111,21 +113,30 @@ octave_child { public: - - typedef void (*dead_child_handler) (pid_t, int); + + // Do whatever to handle event for child with PID (might not + // actually be dead, could just be stopped). Return true if + // the list element corresponding to PID should be removed from + // list. This function should not call any functions that modify + // the octave_child_list. - octave_child (pid_t id = -1, dead_child_handler f = 0) - : pid (id), handler (f) { } + typedef bool (*child_event_handler) (pid_t, int); + + octave_child (pid_t id = -1, child_event_handler f = 0) + : pid (id), handler (f), have_status (0), status (0) { } octave_child (const octave_child& oc) - : pid (oc.pid), handler (oc.handler) { } - + : pid (oc.pid), handler (oc.handler), + have_status (oc.have_status), status (oc.status) { } + octave_child& operator = (const octave_child& oc) { if (&oc != this) { pid = oc.pid; handler = oc.handler; + have_status = oc.have_status; + status = oc.status; } return *this; } @@ -135,8 +146,15 @@ // The process id of this child. pid_t pid; - // The function we call if this child dies. - dead_child_handler handler; + // The function we call if an event happens for this child. + child_event_handler handler; + + // Nonzero if this child has stopped or terminated. + sig_atomic_t have_status; + + // The status of this child; 0 if running, otherwise a status value + // from waitpid. + int status; }; class @@ -144,37 +162,36 @@ { protected: - octave_child_list (void) : list (0), curr_len (0) { } + octave_child_list (void) { } + + class octave_child_list_rep : public octave_base_list + { + public: + + void insert (pid_t pid, octave_child::child_event_handler f); + + void reap (void); + + bool wait (void); + }; public: - ~octave_child_list (void); + ~octave_child_list (void) { } + + static void insert (pid_t pid, octave_child::child_event_handler f); - static bool instance_ok (void); + static void reap (void); - static void insert (pid_t pid, octave_child::dead_child_handler f); + static bool wait (void); static void remove (pid_t pid); - static int length (void); - - static octave_child& elem (int i); - private: - Array list; - - int curr_len; - - static octave_child_list *instance; + static bool instance_ok (void); - void do_insert (pid_t pid, octave_child::dead_child_handler f); - - void do_remove (pid_t pid); - - int do_length (void) const; - - octave_child& do_elem (int i); + static octave_child_list_rep *instance; }; #endif diff --git a/src/toplev.cc b/src/toplev.cc --- a/src/toplev.cc +++ b/src/toplev.cc @@ -104,6 +104,7 @@ can_interrupt = true; octave_interrupt_immediately = 0; octave_interrupt_state = 0; + octave_signal_caught = 0; octave_allocation_error = 0; octave_restore_signal_mask (); octave_catch_interrupts (); @@ -128,6 +129,7 @@ can_interrupt = true; + octave_signal_hook = octave_signal_handler; octave_interrupt_hook = unwind_protect::run_all; octave_bad_alloc_hook = unwind_protect::run_all;