diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/sighandler | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/sighandler')
-rw-r--r-- | library/cpp/sighandler/async_signals_handler.cpp | 245 | ||||
-rw-r--r-- | library/cpp/sighandler/async_signals_handler.h | 14 | ||||
-rw-r--r-- | library/cpp/sighandler/ya.make | 10 |
3 files changed, 269 insertions, 0 deletions
diff --git a/library/cpp/sighandler/async_signals_handler.cpp b/library/cpp/sighandler/async_signals_handler.cpp new file mode 100644 index 0000000000..00ce1c18fb --- /dev/null +++ b/library/cpp/sighandler/async_signals_handler.cpp @@ -0,0 +1,245 @@ +#include "async_signals_handler.h" + +#include <util/system/platform.h> + +#if !defined(_win_) + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> + +#include <unistd.h> + +#if defined(_linux_) +#include <dlfcn.h> +#endif + +#include <util/system/atomic.h> +#include <util/system/defaults.h> +#include <util/system/event.h> +#include <util/system/rwlock.h> +#include <util/system/spinlock.h> +#include <util/system/thread.h> +#include <util/system/yassert.h> +#include <util/generic/hash.h> + +namespace { + volatile int SIGNAL_PIPE_WRITE_FD = 0; // will be initialized in ctor + + void WriteAllOrDie(const int fd, const void* buf, size_t bufsize) { + size_t totalBytesWritten = 0; + + while (totalBytesWritten != bufsize) { + const ssize_t result = write(fd, (const char*)buf + totalBytesWritten, bufsize - totalBytesWritten); + + Y_VERIFY(result >= 0 || (result == -1 && errno == EINTR), "write failed: %s (errno = %d)", strerror(errno), errno); + totalBytesWritten += static_cast<size_t>(result); + } + } + + void PipeWriterSignalHandler(int, siginfo_t* info, void*) { + const ui8 signum = static_cast<ui8>(info->si_signo); + + WriteAllOrDie(SIGNAL_PIPE_WRITE_FD, &signum, 1); + } + + // Handler for the "asynchronous" unix signals (those which can occur + // at arbitrary point of execution and have no need to be reacted on instantly + // and/or to preserve execution context at the point of interrupt). + // + // Async signals -- SIGHUP, SIGUSR1 (used to cause configuration files reread for example) + // Sync signals -- fatal errors like SIGSEGV, SIGBUS... + class TAsyncSignalsHandler { + private: + TThread Thread; + int SignalPipeReadFd; + typedef TAutoPtr<TEventHandler> TEventHandlerPtr; + THashMap<int, TEventHandlerPtr> Handlers; + TRWMutex HandlersLock; + + TAtomic ShouldDie; + TSystemEvent DieEvent; + + static void* ThreadFunc(void* data) { + reinterpret_cast<TAsyncSignalsHandler*>(data)->RealThreadFunc(); + + return nullptr; + } + + inline void RealThreadFunc() { + for (;;) { + ui8 signum; + const ssize_t bytesRead = read(SignalPipeReadFd, &signum, 1); + + Y_VERIFY(bytesRead >= 0 || (bytesRead == -1 && errno == EINTR), "read failed: %s (errno = %d)", strerror(errno), errno); + + if (AtomicAdd(ShouldDie, 0) != 0) { + DieEvent.Signal(); + + break; + } + + if (bytesRead == 0) { + break; + } else if (bytesRead == -1) { + continue; + } + + { + TReadGuard dnd(HandlersLock); + + const TEventHandlerPtr* handler = Handlers.FindPtr(signum); + Y_VERIFY(handler && handler->Get(), "Async signal handler is not set, it's a bug!"); + handler->Get()->Handle(signum); + } + } + } + + public: + TAsyncSignalsHandler() + : Thread(TThread::TParams(ThreadFunc, this).SetName("sighandler")) + , SignalPipeReadFd(0) + , ShouldDie(0) + { + int filedes[2] = {-1}; + +#ifdef _linux_ + int result; + + { + using pipe2_t = decltype(pipe2); + pipe2_t* pipe2Ptr = (pipe2_t*)dlsym(RTLD_DEFAULT, "pipe2"); + +#if defined(_musl_) + if (!pipe2Ptr) { + pipe2Ptr = pipe2; + } +#endif + + if (pipe2Ptr) { + result = pipe2Ptr(filedes, O_CLOEXEC); + } else { + result = -1; + errno = ENOSYS; + } + } + + if (result != 0 && errno == ENOSYS) { // linux older than 2.6.27 returns "not implemented" +#endif + Y_VERIFY(pipe(filedes) == 0, "pipe failed: %s (errno = %d)", strerror(errno), errno); + + SignalPipeReadFd = filedes[0]; + SIGNAL_PIPE_WRITE_FD = filedes[1]; + + Y_VERIFY(fcntl(SignalPipeReadFd, F_SETFD, FD_CLOEXEC) == 0, "fcntl failed: %s (errno = %d)", strerror(errno), errno); + Y_VERIFY(fcntl(SIGNAL_PIPE_WRITE_FD, F_SETFD, FD_CLOEXEC) == 0, "fcntl failed: %s (errno = %d)", strerror(errno), errno); +#ifdef _linux_ + } else { + Y_VERIFY(result == 0, "pipe2 failed: %s (errno = %d)", strerror(errno), errno); + SignalPipeReadFd = filedes[0]; + SIGNAL_PIPE_WRITE_FD = filedes[1]; + } +#endif + + Thread.Start(); + Thread.Detach(); + } + + ~TAsyncSignalsHandler() { + AtomicSwap(&ShouldDie, TAtomic(1)); + ui8 fakeSignal = 0; + WriteAllOrDie(SIGNAL_PIPE_WRITE_FD, &fakeSignal, 1); + + DieEvent.WaitT(TDuration::Seconds(15)); + + /* may cause VERIFY failure in signal handler, propably we should leave it to process clean procedure + close(SIGNAL_PIPE_WRITE_FD); + close(SignalPipeReadFd); +*/ + } + + bool DoInstall(int signum, TAutoPtr<TEventHandler> handler) { + TWriteGuard dnd(HandlersLock); + TEventHandlerPtr& ev = Handlers[signum]; + const bool ret = !ev; + + ev = handler; + + return ret; + } + + void Install(int signum, TAutoPtr<TEventHandler> handler) { + if (DoInstall(signum, handler)) { + struct sigaction a; + + memset(&a, 0, sizeof(a)); + a.sa_sigaction = PipeWriterSignalHandler; + a.sa_flags = SA_SIGINFO | SA_RESTART; + + Y_VERIFY(!sigaction(signum, &a, nullptr), "sigaction failed: %s (errno = %d)", strerror(errno), errno); + } + } + }; + + // This pointer is never deleted - yeah, it's intended memory leak. + // It is necessary to prevent problems when user's signal handler calls exit function + // which destroys all global variables including this one. + // It such situation we have 2 options: + // - wait for auxiliary thread to die - which will cause dead lock + // - destruct variable, ignoring thread - which will cause data corruption. + TAsyncSignalsHandler* SIGNALS_HANDLER = nullptr; +} + +void SetAsyncSignalHandler(int signum, TAutoPtr<TEventHandler> handler) { + static TAtomic lock; + + if (Y_UNLIKELY(SIGNALS_HANDLER == nullptr)) { + TGuard<TAtomic> dnd(lock); + + if (SIGNALS_HANDLER == nullptr) { + // NEVERS GETS DESTROYED + SIGNALS_HANDLER = new TAsyncSignalsHandler(); + } + } + + SIGNALS_HANDLER->Install(signum, handler); +} + +#else //_win_ + +void SetAsyncSignalHandler(int, TAutoPtr<TEventHandler>) { + // TODO: it's really easy to port using _pipe, _read and _write, but it must be tested properly. +} + +#endif + +namespace { + template <typename TFunc> + class TFunctionEventHandler: public TEventHandler { + TFunc Func; + + public: + TFunctionEventHandler(TFunc func) { + if (func) + Func = func; + } + + int Handle(int signum) override { + if (Func) { + Func(signum); + } + + return 0; + } + }; +} + +void SetAsyncSignalHandler(int signum, void (*handler)(int)) { + SetAsyncSignalHandler(signum, new TFunctionEventHandler<void (*)(int)>(handler)); +} + +void SetAsyncSignalFunction(int signum, std::function<void(int)> func) { + typedef std::function<void(int)> TFunc; + SetAsyncSignalHandler(signum, new TFunctionEventHandler<TFunc>(func)); +} diff --git a/library/cpp/sighandler/async_signals_handler.h b/library/cpp/sighandler/async_signals_handler.h new file mode 100644 index 0000000000..da36365ace --- /dev/null +++ b/library/cpp/sighandler/async_signals_handler.h @@ -0,0 +1,14 @@ +#pragma once + +#include <util/generic/ptr.h> +#include <functional> + +struct TEventHandler { + virtual ~TEventHandler() { + } + virtual int Handle(int signum) = 0; +}; + +void SetAsyncSignalHandler(int signum, TAutoPtr<TEventHandler> handler); +void SetAsyncSignalHandler(int signum, void (*handler)(int)); +void SetAsyncSignalFunction(int signum, std::function<void(int)> func); diff --git a/library/cpp/sighandler/ya.make b/library/cpp/sighandler/ya.make new file mode 100644 index 0000000000..c0f7ea6084 --- /dev/null +++ b/library/cpp/sighandler/ya.make @@ -0,0 +1,10 @@ +LIBRARY() + +OWNER(pg) + +SRCS( + async_signals_handler.cpp + async_signals_handler.h +) + +END() |