From af973bea668db9484001d94e9d6ab3b216acb34e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 21 Feb 2020 00:29:32 -0800 Subject: [PATCH] harmonize signal handling --- src/pvxs/server.h | 4 ++++ src/pvxs/util.h | 23 ++++++++++++++++++++++ src/server.cpp | 50 +++++++---------------------------------------- src/util.cpp | 42 +++++++++++++++++++++++++++++++++++++++ tools/info.cpp | 24 ++++++++++++++++++++--- tools/pvxvct.cpp | 29 ++++++--------------------- 6 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/pvxs/server.h b/src/pvxs/server.h index 135ea77..b313c17 100644 --- a/src/pvxs/server.h +++ b/src/pvxs/server.h @@ -60,6 +60,10 @@ public: * * run() may be interupted by calling interrupt(), * or by SIGINT or SIGTERM (only one Server per process) + * + * Intended to simple CLI programs. + * Only one Server in a process may be in run() at any moment. + * Other use case should call start()/stop() */ Server& run(); //! Queue a request to break run() diff --git a/src/pvxs/util.h b/src/pvxs/util.h index 2561c46..ea06390 100644 --- a/src/pvxs/util.h +++ b/src/pvxs/util.h @@ -7,6 +7,7 @@ #ifndef PVXS_UTIL_H #define PVXS_UTIL_H +#include #include #include @@ -70,6 +71,28 @@ inline detail::Escaper escape(const char* s,size_t n) { return detail::Escaper(s,n); } +#if !defined(__rtems__) && !defined(vxWorks) + +//! minimal portable process signal handling in CLI tools +class PVXS_API SigInt { + void (*prevINT)(int); + void (*prevTERM)(int); + std::function handler; + static void _handle(int); +public: + SigInt(decltype (handler)&& handler); + ~SigInt(); +}; + +#else // !defined(__rtems__) && !defined(vxWorks) + +class SigInt { +public: + SigInt(std::function&& handler) {} +} + +#endif // !defined(__rtems__) && !defined(vxWorks) + //! representation of a network address struct PVXS_API SockAddr { union store_t { diff --git a/src/server.cpp b/src/server.cpp index 03ac49a..17018eb 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -4,12 +4,6 @@ * in file LICENSE that is included with this distribution. */ -#if defined(_WIN32) -# include -#elif !defined(__rtems__) && !defined(vxWorks) -# include -# include -#endif #include #include @@ -177,52 +171,22 @@ Server& Server::stop() return *this; } -static std::atomic sig_target{nullptr}; - -static void sig_handle(int sig) -{ - auto serv = sig_target.load(); - - if(serv) - serv->done.signal(); -} - Server& Server::run() { if(!pvt) throw std::logic_error("NULL Server"); - Server::Pvt* expect = nullptr; + pvt->start(); - std::function cleanup; - if(sig_target.compare_exchange_weak(expect, pvt.get())) { - // we claimed the signal handler slot. - // save previous handlers - auto prevINT = signal(SIGINT , &sig_handle); - auto prevTERM = signal(SIGTERM, &sig_handle); - - cleanup = [this, prevINT, prevTERM]() { - Server::Pvt* expect = pvt.get(); - if(sig_target.compare_exchange_weak(expect, nullptr)) { - signal(SIGINT , prevINT); - signal(SIGTERM, prevTERM); - } - }; - } - - try { - pvt->start(); + { + SigInt handler([this](){ + pvt->done.signal(); + }); pvt->done.wait(); - - pvt->stop(); - } catch(...) { - if(cleanup) - cleanup(); - throw; } - if(cleanup) - cleanup(); + + pvt->stop(); return *this; } diff --git a/src/util.cpp b/src/util.cpp index 0f0c120..228f69f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -4,10 +4,14 @@ * in file LICENSE that is included with this distribution. */ +// for signal handling +#include + #include #include #include #include +#include #include @@ -157,6 +161,44 @@ std::ostream& operator<<(std::ostream& strm, const Escaper& esc) } // namespace detail +#if !defined(__rtems__) && !defined(vxWorks) + +static +std::atomic thesig{nullptr}; + +void SigInt::_handle(int num) +{ + auto sig = thesig.load(); + if(!sig) + return; + + sig->handler(); +} + +SigInt::SigInt(decltype (handler)&& handler) + :handler(std::move(handler)) +{ + // we can't atomically replace multiple signal handler anyway + + SigInt* expect = nullptr; + + if(!thesig.compare_exchange_weak(expect, this)) + throw std::logic_error("Only one SigInt allowed"); + + prevINT = signal(SIGINT, &_handle); + prevTERM = signal(SIGTERM, &_handle); +} + +SigInt::~SigInt() +{ + signal(SIGINT, prevINT); + signal(SIGTERM, prevTERM); + + thesig.store(nullptr); +} + +#endif // !defined(__rtems__) && !defined(vxWorks) + SockAddr::SockAddr(int af) { memset(&store, 0, sizeof(store)); diff --git a/tools/info.cpp b/tools/info.cpp index 64682b1..09ad125 100644 --- a/tools/info.cpp +++ b/tools/info.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -66,16 +67,33 @@ int main(int argc, char *argv[]) std::list> ops; + std::atomic remaining{argc-optind}; + epicsEvent done; + for(auto n : range(optind, argc)) { ops.push_back(ctxt.info(argv[n]) - .result([&argv, n](Value&& prototype) { + .result([&argv, n, &remaining, &done](Value&& prototype) { std::cout< #include -#if !defined(_WIN32) -#include -#define USE_SIGNAL -#endif - #include #include #include @@ -35,16 +30,6 @@ namespace { DEFINE_LOGGER(out, "pvxvct"); -epicsEvent done; - -#ifdef USE_SIGNAL -void alldone(int num) -{ - (void)num; - done.signal(); -} -#endif - // parse hostname, IP, or IP+netmask std::tuple parsePeer(const char *optarg) @@ -195,7 +180,7 @@ int main(int argc, char *argv[]) } } - auto searchCB = [&opts](const pva::UDPManager::Search& msg) + auto searchCB = [](const pva::UDPManager::Search& msg) { log_info_printf(out, "%s Searching for:\n", msg.src.tostring().c_str()); for(const auto pv : msg.names) { @@ -203,7 +188,7 @@ int main(int argc, char *argv[]) } }; - auto beaconCB = [&opts](const pva::UDPManager::Beacon& msg) + auto beaconCB = [](const pva::UDPManager::Beacon& msg) { const auto& guid = msg.guid; log_info_printf(out, "%s Beacon %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %s\n", @@ -226,12 +211,10 @@ int main(int argc, char *argv[]) } - -#ifdef USE_SIGNAL - signal(SIGINT, alldone); - signal(SIGTERM, alldone); - signal(SIGQUIT, alldone); -#endif + epicsEvent done; + pva::SigInt handle([&done](){ + done.trigger(); + }); done.wait(); log_info_printf(out, "Done\n%s", "");