diff --git a/qse/include/qse/si/App.hpp b/qse/include/qse/si/App.hpp index 7c35cfc0..0d35cda1 100644 --- a/qse/include/qse/si/App.hpp +++ b/qse/include/qse/si/App.hpp @@ -38,8 +38,8 @@ QSE_BEGIN_NAMESPACE(QSE) class App: public Uncopyable, public Types, public Mmged { public: - App (Mmgr* mmgr) QSE_CPP_NOEXCEPT: Mmged(mmgr), _root_only(false) {} - ~App () QSE_CPP_NOEXCEPT {} + App (Mmgr* mmgr) QSE_CPP_NOEXCEPT; + virtual ~App () QSE_CPP_NOEXCEPT; int daemonize (bool chdir_to_root = true, int fork_count = 1) QSE_CPP_NOEXCEPT; @@ -51,14 +51,62 @@ public: int restoreUser () QSE_CPP_NOEXCEPT; #endif + virtual void on_signal (int sig) { } + protected: bool _root_only; +private: + App* _prev_app; + App* _next_app; + public: + struct _SigLink + { + enum + { + UNHANDLED, + ACCEPTED, + IGNORED // handled but ignored + }; + + _SigLink(): _prev(QSE_NULL), _next(QSE_NULL), _state(UNHANDLED) {} + App* _prev; + App* _next; + int _state; + }; + + _SigLink _sig[QSE_NSIGS]; + +public: + typedef void (*SignalHandler) (int sig); static int setSignalHandler (int sig, SignalHandler sighr); static int unsetSignalHandler (int sig); static qse_size_t _sighrs[2][QSE_NSIGS]; + + void handleSignal (int sig, bool accept); + void unhandleSignal (int sig); +}; + +// functor as a template parameter +template +class QSE_EXPORT AppF: public App +{ +public: + AppF (Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT: App(mmgr) {} + AppF (const F& f, Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT: App(mmgr), __lfunc(f) {} +#if defined(QSE_CPP_ENABLE_CPP11_MOVE) + AppF (F&& f, Mmgr* mmgr = QSE_NULL) QSE_CPP_NOEXCEPT: App(mmgr), __lfunc(QSE_CPP_RVREF(f)) {} +#endif + +protected: + F __lfunc; + + void on_signal (int sig) + { + this->__lfunc(this, sig); + } }; ///////////////////////////////// diff --git a/qse/lib/si/App.cpp b/qse/lib/si/App.cpp index 25ca1d0b..b34c769f 100644 --- a/qse/lib/si/App.cpp +++ b/qse/lib/si/App.cpp @@ -25,6 +25,7 @@ */ #include +#include #include #include "../cmn/syscall.h" #include @@ -33,6 +34,38 @@ QSE_BEGIN_NAMESPACE(QSE) ///////////////////////////////// + +static Mutex g_app_mutex; +static App* g_app_top = QSE_NULL; // maintain the chain of application objects +static App* g_app_sig[QSE_NSIGS] = { QSE_NULL, }; +static struct +{ + sigset_t sa_mask; + int sa_flags; +} g_app_oldsi[QSE_NSIGS] = { { 0, 0 }, }; + +App::App (Mmgr* mmgr) QSE_CPP_NOEXCEPT: Mmged(mmgr), _root_only(false), _prev_app(QSE_NULL), _next_app(QSE_NULL) +{ + ScopedMutexLocker sml(g_app_mutex); + if (!g_app_top) + { + g_app_top = this; + } + else + { + g_app_top->_prev_app = this; + this->_next_app = g_app_top; + } +} + +App::~App () QSE_CPP_NOEXCEPT +{ + ScopedMutexLocker sml(g_app_mutex); + 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) g_app_top = this->_next_app; +} + int App::daemonize (bool chdir_to_root, int fork_count) QSE_CPP_NOEXCEPT { if (this->_root_only && QSE_GETEUID() != 0) return -1; @@ -102,7 +135,6 @@ int App::daemonize (bool chdir_to_root, int fork_count) QSE_CPP_NOEXCEPT } - int App::chroot (const qse_mchar_t* mpath) QSE_CPP_NOEXCEPT { return QSE_CHROOT (mpath); @@ -169,14 +201,6 @@ static void dispatch_siginfo (int sig, siginfo_t* si, void* ctx) } } -// i don't want to use sigset_t in App.hpp. -// so i place oldsi here as a static variable instead of -// static member variable of App -static struct -{ - sigset_t sa_mask; - int sa_flags; -} oldsi[QSE_NSIGS] = { { 0, 0 }, }; int App::setSignalHandler (int sig, SignalHandler sighr) { @@ -205,8 +229,8 @@ int App::setSignalHandler (int sig, SignalHandler sighr) App::_sighrs[0][sig] = (qse_size_t)sighr; App::_sighrs[1][sig] = (qse_size_t)oldsa.sa_handler; - oldsi[sig].sa_mask = oldsa.sa_mask; - oldsi[sig].sa_flags = oldsa.sa_flags; + g_app_oldsi[sig].sa_mask = oldsa.sa_mask; + g_app_oldsi[sig].sa_flags = oldsa.sa_flags; return 0; } @@ -217,20 +241,97 @@ int App::unsetSignalHandler (int sig) struct sigaction sa; - sa.sa_mask = oldsi[sig].sa_mask; - sa.sa_flags = oldsi[sig].sa_flags; + sa.sa_mask = g_app_oldsi[sig].sa_mask; + sa.sa_flags = g_app_oldsi[sig].sa_flags; if (sa.sa_flags & SA_SIGINFO) sa.sa_sigaction = (void(*)(int,siginfo_t*,void*))App::_sighrs[1][sig]; else sa.sa_handler = (SignalHandler)App::_sighrs[1][sig]; - if (sigaction (sig, &sa, QSE_NULL) <= -1) return -1; + if (::sigaction (sig, &sa, QSE_NULL) <= -1) return -1; App::_sighrs[0][sig] = 0; App::_sighrs[1][sig] = 0; return 0; } +static void handle_signal (int sig) +{ + ScopedMutexLocker sml(g_app_mutex); + App* app = g_app_sig[sig]; + while (app) + { + App::_SigLink& sl = app->_sig[sig]; + App* next = sl._next; + if (sl._state == App::_SigLink::ACCEPTED) app->on_signal (sig); + app = next; + } +} + +void App::handleSignal (int sig, bool accept) +{ + QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); + + int reqstate = accept? _SigLink::ACCEPTED: _SigLink::IGNORED; + ScopedMutexLocker sml(g_app_mutex); + + _SigLink& sl = this->_sig[sig]; + if (QSE_LIKELY(sl._state == _SigLink::UNHANDLED)) + { + if (!g_app_sig[sig]) + { + // no application is set to accept this signal. + // this is the first time to + App::setSignalHandler (sig, handle_signal); + } + + sl._state = _SigLink::ACCEPTED; + sl._next = g_app_sig[sig]; + g_app_sig[sig] = this; + } + else + { + // already configured to receive the signal. + QSE_ASSERT (g_app_sig[sig] != QSE_NULL); + sl._state = reqstate; + } +} + +void App::unhandleSignal (int sig) +{ + QSE_ASSERT (sig >= 0 && sig < QSE_NSIGS); + + ScopedMutexLocker sml(g_app_mutex); + + _SigLink& sl = this->_sig[sig]; + if (QSE_UNLIKELY(sl._state == _SigLink::UNHANDLED)) + { + QSE_ASSERT (g_app_sig[sig] != this); + QSE_ASSERT (sl._prev == QSE_NULL && sl._next == QSE_NULL); + // nothing to do + } + else + { + QSE_ASSERT (g_app_sig[sig] != QSE_NULL); + + if (g_app_sig[sig] == this) g_app_sig[sig] = sl._next; + + if (sl._next) + { + sl._next->_sig[sig]._prev = sl._prev; + sl._next = QSE_NULL; + } + if (sl._prev) + { + sl._prev->_sig[sig]._next = sl._next; + sl._prev = QSE_NULL; + } + + if (!g_app_sig[sig]) App::unsetSignalHandler (sig); + sl._state = _SigLink::UNHANDLED; + } +} + ///////////////////////////////// QSE_END_NAMESPACE(QSE) ///////////////////////////////// diff --git a/qse/samples/si/tcpsvr01.cpp b/qse/samples/si/tcpsvr01.cpp index e732dae4..9201f34d 100644 --- a/qse/samples/si/tcpsvr01.cpp +++ b/qse/samples/si/tcpsvr01.cpp @@ -18,10 +18,6 @@ QSE::Mutex g_prt_mutex; -#if defined(QSE_LANG_CPP11) -QSE::TcpServerL* g_server; -#else - class ClientHandler { public: @@ -55,10 +51,83 @@ public: } }; +#if defined(QSE_LANG_CPP11) +static QSE::TcpServerL* g_server; +#else static QSE::TcpServerF* g_server; #endif +class MyApp: public QSE::App +{ +public: + MyApp(QSE::Mmgr* mmgr): App(mmgr), server(mmgr) {} + + void on_signal (int sig) + { + switch (sig) + { + case SIGINT: + case SIGTERM: + case SIGHUP: + this->server.stop(); + break; + } + } + + int run () + { + this->server.setThreadStackSize (256000); + return this->server.start (QSE_T("[::]:9998,0.0.0.0:9998")); + } + +protected: + QSE::TcpServerF server; +}; + +static int test1() +{ + QSE::HeapMmgr heap_mmgr (QSE::Mmgr::getDFL(), 30000); + MyApp app (&heap_mmgr); + app.handleSignal (SIGINT, true); + app.handleSignal (SIGTERM, true); + int n = app.run(); + app.handleSignal (SIGTERM, false); + app.handleSignal (SIGINT, false); + return n; +} + +int main () +{ +#if defined(_WIN32) + char locale[100]; + UINT codepage = GetConsoleOutputCP(); + if (codepage == CP_UTF8) + { + /*SetConsoleOUtputCP (CP_UTF8);*/ + qse_setdflcmgrbyid (QSE_CMGR_UTF8); + } + else + { + sprintf (locale, ".%u", (unsigned int)codepage); + setlocale (LC_ALL, locale); + /*qse_setdflcmgrbyid (QSE_CMGR_SLMB);*/ + } +#else + setlocale (LC_ALL, ""); + /*qse_setdflcmgrbyid (QSE_CMGR_SLMB);*/ +#endif + + qse_open_stdsios (); + test1(); + qse_close_stdsios (); + + return 0; +} + + +#if 0 //////////////////////// + static int test1 (void) { QSE::HeapMmgr heap_mmgr (QSE::Mmgr::getDFL(), 30000); @@ -96,7 +165,6 @@ static int test1 (void) }), &heap_mmgr - ); #else QSE::TcpServerF server (&heap_mmgr); @@ -139,12 +207,13 @@ int main () qse_open_stdsios (); - QSE::App::setSignalHandler (SIGINT, handle_sigint); + //QSE::App::setSignalHandler (SIGINT, handle_sigint); test1(); - QSE::App::unsetSignalHandler (SIGINT); + //QSE::App::unsetSignalHandler (SIGINT); qse_close_stdsios (); - return 0; } + +#endif ////////////////////////