diff --git a/qse/include/qse/cmn/opt.h b/qse/include/qse/cmn/opt.h index fae2a5c3..82e1ded2 100644 --- a/qse/include/qse/cmn/opt.h +++ b/qse/include/qse/cmn/opt.h @@ -102,6 +102,7 @@ struct qse_cli_data_t const qse_char_t** optsta; const qse_char_t* optasn; qse_cli_opt_t* opts; + void* ctx; }; typedef struct qse_cli_data_t qse_cli_data_t; diff --git a/qse/include/qse/si/App.hpp b/qse/include/qse/si/App.hpp index 379d76d6..0789ffd3 100644 --- a/qse/include/qse/si/App.hpp +++ b/qse/include/qse/si/App.hpp @@ -43,12 +43,11 @@ public: enum SignalState { - SIGNAL_UNHANDLED, - SIGNAL_ACCEPTED, - SIGNAL_IGNORED // handled but ignored + SIGNAL_NEGLECTED, // signal is unhandled at the system level. + SIGNAL_ACCEPTED, // on_signal callback is triggered + SIGNAL_DISCARDED // handled but doesn't trigger the on_signal callback }; - App (Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT; virtual ~App () QSE_CPP_NOEXCEPT; @@ -65,15 +64,21 @@ public: virtual void on_signal (int sig) { } SignalState getSignalSubscription (int sig) const; - int setSignalSubscription (int sig, SignalState ss); + int setSignalSubscription (int sig, SignalState ss, bool ignore_if_unhandled = false); - int subscribeToSignal (int sig, bool accept) + int acceptSignal (int sig) { - return this->setSignalSubscription (sig, (accept? SIGNAL_ACCEPTED: SIGNAL_IGNORED)); + return this->setSignalSubscription(sig, SIGNAL_ACCEPTED); } - int unsubscribeFromSignal (int sig) + + int discardSignal (int sig) { - return this->setSignalSubscription (sig, SIGNAL_UNHANDLED); + return this->setSignalSubscription(sig, SIGNAL_DISCARDED); + } + + int neglectSignal (int sig) + { + return this->setSignalSubscription(sig, SIGNAL_NEGLECTED); } typedef void (*SignalHandler) (int sig); @@ -96,7 +101,7 @@ private: struct _SigLink { - _SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(SIGNAL_UNHANDLED) {} + _SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(SIGNAL_NEGLECTED) {} App* _prev; App* _next; SignalState _state; @@ -105,15 +110,12 @@ private: _SigLink _sig[QSE_NSIGS]; long int _guarded_child_pid; -protected: static int set_signal_handler_no_mutex (int sig, SignalHandler sighr); static int unset_signal_handler_no_mutex (int sig, int ignore); - - int set_signal_subscription_no_mutex (int sig, SignalState reqstate); + int set_signal_subscription_no_mutex (int sig, SignalState reqstate, bool ignore_if_unhandled); void on_guard_signal (int sig); static void handle_signal (int sig); - }; ///////////////////////////////// diff --git a/qse/lib/si/App.cpp b/qse/lib/si/App.cpp index 5c122256..bc6bfd79 100644 --- a/qse/lib/si/App.cpp +++ b/qse/lib/si/App.cpp @@ -85,8 +85,19 @@ App::~App () QSE_CPP_NOEXCEPT SigScopedMutexLocker sml(g_app_mutex); for (int i = 0; i < QSE_NSIGS; i++) { - this->set_signal_subscription_no_mutex (i, SIGNAL_UNHANDLED); + // if the signal handler has not been removed before this application + // is destroyed and this is the last application subscribing to + // a signal, i arrange to set the signal handler to SIG_IGN as indicated by + // the third argument 'true' to set_signal_subscription_no_mutex(). + // if the signal is not ignored, the signal received after destruction + // of this application object may lead to program crash as the handler + // is still associated with the destructed object. + + // if the signal handler has been removed, the following function + // actual does nothing. + this->set_signal_subscription_no_mutex (i, SIGNAL_NEGLECTED, true); } + if (this->_next_app) this->_next_app->_prev_app = this->_prev_app; if (this->_prev_app) this->_prev_app->_next_app = this->_next_app; if (this == g_app_top) @@ -321,10 +332,10 @@ int App::unset_signal_handler_no_mutex(int sig, int ignore) if (sl._state == App::SIGNAL_ACCEPTED) { // the actual signal handler is called with the mutex locked. - // it must not call subscribeToSingal() or unsubscribeFromSingal() + // it must not call acceptSingal()/discardSignal()neglectSingal() // from within the handler. if (app->_guarded_child_pid >= 0) app->on_guard_signal (sig); - else app->on_signal (sig); + app->on_signal (sig); } app = next; } @@ -335,28 +346,28 @@ void App::on_guard_signal (int sig) ::kill (this->_guarded_child_pid, sig); } - App::SignalState App::getSignalSubscription (int sig) const { QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); return this->_sig[sig]._state; } -int App::setSignalSubscription (int sig, SignalState ss) +int App::setSignalSubscription (int sig, SignalState ss, bool ignore_if_unhandled) { QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); SigScopedMutexLocker sml(g_app_mutex); - return this->set_signal_subscription_no_mutex(sig, ss); + return this->set_signal_subscription_no_mutex(sig, ss, ignore_if_unhandled); } -int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) +int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate, bool ignore_if_unhandled) { _SigLink& sl = this->_sig[sig]; + if (QSE_UNLIKELY(sl._state == reqstate)) return 0; // no change - if (reqstate == SIGNAL_UNHANDLED) + if (reqstate == SIGNAL_NEGLECTED) { - // accepted/ignored -> unhandled + // accepted/discarded -> neglected(unhandled) QSE_ASSERT (g_app_sig[sig] != QSE_NULL); if (g_app_sig[sig] == this) @@ -364,7 +375,9 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) QSE_ASSERT (sl._prev == QSE_NULL); if (!sl._next) { - if (App::unset_signal_handler_no_mutex(sig, true) <= -1) return -1; + // this is the last application subscribing to the signal. + // let's get rid of the signal handler + if (App::unset_signal_handler_no_mutex(sig, ignore_if_unhandled) <= -1) return -1; } g_app_sig[sig] = sl._next; } @@ -375,9 +388,9 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) sl._next = QSE_NULL; sl._state = reqstate; } - else if (QSE_LIKELY(sl._state == SIGNAL_UNHANDLED)) + else if (QSE_LIKELY(sl._state == SIGNAL_NEGLECTED)) { - // unhandled -> accepted/ignored + // neglected(unhandled) -> accepted/discarded QSE_ASSERT (sl._prev == QSE_NULL && sl._next == QSE_NULL); App* xapp = g_app_sig[sig]; @@ -400,7 +413,7 @@ int App::set_signal_subscription_no_mutex (int sig, SignalState reqstate) // roll back g_app_sig[sig] = xapp; if (xapp) xapp->_sig[sig]._prev = xapp_xprev; - sl._state = SIGNAL_UNHANDLED; + sl._state = SIGNAL_NEGLECTED; sl._next = QSE_NULL; QSE_ASSERT (sl._prev == QSE_NULL); return -1; @@ -423,25 +436,36 @@ int App::guardProcess (const SignalSet& signals, const qse_mchar_t* proc_name) { SignalState old_ss[QSE_NSIGS]; + for (int i = 0; i < QSE_NSIGS; i++) + { + if (signals.isSet(i)) + { + old_ss[i] = this->getSignalSubscription(i); + this->setSignalSubscription (i, SIGNAL_ACCEPTED); + } + } + while (1) { pid_t pid = ::fork(); if (pid == -1) return -1; if (pid == 0) { - ::setpgid (0, 0); // change the process group id. - break; // child - } + // child process + this->_guarded_child_pid = -1; - for (int i = 0; i < QSE_NSIGS; i++) - { - if (signals.isSet(i)) + for (int i = 0; i < QSE_NSIGS; i++) { - old_ss[i] = this->getSignalSubscription(i); - this->setSignalSubscription (i, SIGNAL_ACCEPTED); + if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); } + + ::setpgid (0, 0); // change the process group id. + break; } + // =============================================== + // the guardian(parent) process + // =============================================== this->_guarded_child_pid = pid; int status; @@ -455,17 +479,15 @@ int App::guardProcess (const SignalSet& signals, const qse_mchar_t* proc_name) // ------------------------------------------------------ } - this->_guarded_child_pid = -1; - for (int i = 0; i < QSE_NSIGS; i++) - { - if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); - } - if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { // the child has terminated normally and successfully. + for (int i = 0; i < QSE_NSIGS; i++) + { + if (signals.isSet(i)) this->setSignalSubscription (i, old_ss[i]); + } return 0; } } diff --git a/qse/samples/si/tcpsvr01.cpp b/qse/samples/si/tcpsvr01.cpp index 44b5e751..70ceaac2 100644 --- a/qse/samples/si/tcpsvr01.cpp +++ b/qse/samples/si/tcpsvr01.cpp @@ -67,6 +67,8 @@ public: case SIGINT: case SIGTERM: case SIGHUP: + // TODO: don't call stop() if this processs is a guardian + // though it's no harm to call stop(). qse_printf (QSE_T("requesting to stop server...app %p server %p - pid %d\n"), this, &this->server, (int)getpid()); this->server.stop(); break; @@ -103,20 +105,20 @@ MyApp app2 (&heap_mmgr); MyApp app3 (&heap_mmgr); MyApp app4 (&heap_mmgr); - app.subscribeToSignal (SIGINT, true); - app.subscribeToSignal (SIGTERM, true); + app.acceptSignal (SIGINT); + app.acceptSignal (SIGTERM); -app4.subscribeToSignal (SIGINT, true); -app3.subscribeToSignal (SIGINT, true); -app2.subscribeToSignal (SIGINT, true); +app4.acceptSignal (SIGINT); +app3.acceptSignal (SIGINT); +app2.acceptSignal (SIGINT); int n = app.run(); - app.subscribeToSignal (SIGTERM, false); - app.subscribeToSignal (SIGINT, false); + app.discardSignal (SIGTERM); + app.discardSignal (SIGINT); -app4.unsubscribeFromSignal (SIGINT); -app3.unsubscribeFromSignal (SIGINT); -app2.unsubscribeFromSignal (SIGINT); +app4.neglectSignal (SIGINT); +app3.neglectSignal (SIGINT); +app2.neglectSignal (SIGINT); qse_printf (QSE_T("END OF %d\n"), (int)getpid()); return n;