aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/sighandler
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/sighandler
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/sighandler')
-rw-r--r--library/cpp/sighandler/async_signals_handler.cpp245
-rw-r--r--library/cpp/sighandler/async_signals_handler.h14
-rw-r--r--library/cpp/sighandler/ya.make10
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()