async_signals_handler.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #include "async_signals_handler.h"
  2. #include <util/system/platform.h>
  3. #if !defined(_win_)
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <signal.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #if defined(_linux_)
  10. #include <dlfcn.h>
  11. #endif
  12. #include <library/cpp/deprecated/atomic/atomic.h>
  13. #include <util/system/defaults.h>
  14. #include <util/system/event.h>
  15. #include <util/system/rwlock.h>
  16. #include <util/system/spinlock.h>
  17. #include <util/system/thread.h>
  18. #include <util/system/yassert.h>
  19. #include <util/generic/hash.h>
  20. namespace {
  21. volatile int SIGNAL_PIPE_WRITE_FD = 0; // will be initialized in ctor
  22. void WriteAllOrDie(const int fd, const void* buf, size_t bufsize) {
  23. size_t totalBytesWritten = 0;
  24. while (totalBytesWritten != bufsize) {
  25. const ssize_t result = write(fd, (const char*)buf + totalBytesWritten, bufsize - totalBytesWritten);
  26. Y_ABORT_UNLESS(result >= 0 || (result == -1 && errno == EINTR), "write failed: %s (errno = %d)", strerror(errno), errno);
  27. totalBytesWritten += static_cast<size_t>(result);
  28. }
  29. }
  30. void PipeWriterSignalHandler(int, siginfo_t* info, void*) {
  31. const ui8 signum = static_cast<ui8>(info->si_signo);
  32. WriteAllOrDie(SIGNAL_PIPE_WRITE_FD, &signum, 1);
  33. }
  34. // Handler for the "asynchronous" unix signals (those which can occur
  35. // at arbitrary point of execution and have no need to be reacted on instantly
  36. // and/or to preserve execution context at the point of interrupt).
  37. //
  38. // Async signals -- SIGHUP, SIGUSR1 (used to cause configuration files reread for example)
  39. // Sync signals -- fatal errors like SIGSEGV, SIGBUS...
  40. class TAsyncSignalsHandler {
  41. private:
  42. TThread Thread;
  43. int SignalPipeReadFd;
  44. typedef THolder<TEventHandler> TEventHandlerPtr;
  45. THashMap<int, TEventHandlerPtr> Handlers;
  46. TRWMutex HandlersLock;
  47. TAtomic ShouldDie;
  48. TSystemEvent DieEvent;
  49. static void* ThreadFunc(void* data) {
  50. reinterpret_cast<TAsyncSignalsHandler*>(data)->RealThreadFunc();
  51. return nullptr;
  52. }
  53. inline void RealThreadFunc() {
  54. for (;;) {
  55. ui8 signum;
  56. const ssize_t bytesRead = read(SignalPipeReadFd, &signum, 1);
  57. Y_ABORT_UNLESS(bytesRead >= 0 || (bytesRead == -1 && errno == EINTR), "read failed: %s (errno = %d)", strerror(errno), errno);
  58. if (AtomicAdd(ShouldDie, 0) != 0) {
  59. DieEvent.Signal();
  60. break;
  61. }
  62. if (bytesRead == 0) {
  63. break;
  64. } else if (bytesRead == -1) {
  65. continue;
  66. }
  67. {
  68. TReadGuard dnd(HandlersLock);
  69. const TEventHandlerPtr* handler = Handlers.FindPtr(signum);
  70. Y_ABORT_UNLESS(handler && handler->Get(), "Async signal handler is not set, it's a bug!");
  71. handler->Get()->Handle(signum);
  72. }
  73. }
  74. }
  75. public:
  76. TAsyncSignalsHandler()
  77. : Thread(TThread::TParams(ThreadFunc, this).SetName("sighandler"))
  78. , SignalPipeReadFd(0)
  79. , ShouldDie(0)
  80. {
  81. int filedes[2] = {-1};
  82. #ifdef _linux_
  83. int result;
  84. {
  85. using pipe2_t = decltype(pipe2);
  86. pipe2_t* pipe2Ptr = (pipe2_t*)dlsym(RTLD_DEFAULT, "pipe2");
  87. #if defined(_musl_)
  88. if (!pipe2Ptr) {
  89. pipe2Ptr = pipe2;
  90. }
  91. #endif
  92. if (pipe2Ptr) {
  93. result = pipe2Ptr(filedes, O_CLOEXEC);
  94. } else {
  95. result = -1;
  96. errno = ENOSYS;
  97. }
  98. }
  99. if (result != 0 && errno == ENOSYS) { // linux older than 2.6.27 returns "not implemented"
  100. #endif
  101. Y_ABORT_UNLESS(pipe(filedes) == 0, "pipe failed: %s (errno = %d)", strerror(errno), errno);
  102. SignalPipeReadFd = filedes[0];
  103. SIGNAL_PIPE_WRITE_FD = filedes[1];
  104. Y_ABORT_UNLESS(fcntl(SignalPipeReadFd, F_SETFD, FD_CLOEXEC) == 0, "fcntl failed: %s (errno = %d)", strerror(errno), errno);
  105. Y_ABORT_UNLESS(fcntl(SIGNAL_PIPE_WRITE_FD, F_SETFD, FD_CLOEXEC) == 0, "fcntl failed: %s (errno = %d)", strerror(errno), errno);
  106. #ifdef _linux_
  107. } else {
  108. Y_ABORT_UNLESS(result == 0, "pipe2 failed: %s (errno = %d)", strerror(errno), errno);
  109. SignalPipeReadFd = filedes[0];
  110. SIGNAL_PIPE_WRITE_FD = filedes[1];
  111. }
  112. #endif
  113. Thread.Start();
  114. Thread.Detach();
  115. }
  116. ~TAsyncSignalsHandler() {
  117. AtomicSwap(&ShouldDie, TAtomic(1));
  118. ui8 fakeSignal = 0;
  119. WriteAllOrDie(SIGNAL_PIPE_WRITE_FD, &fakeSignal, 1);
  120. DieEvent.WaitT(TDuration::Seconds(15));
  121. /* may cause VERIFY failure in signal handler, propably we should leave it to process clean procedure
  122. close(SIGNAL_PIPE_WRITE_FD);
  123. close(SignalPipeReadFd);
  124. */
  125. }
  126. bool DoInstall(int signum, THolder<TEventHandler> handler) {
  127. TWriteGuard dnd(HandlersLock);
  128. TEventHandlerPtr& ev = Handlers[signum];
  129. const bool ret = !ev;
  130. ev = std::move(handler);
  131. return ret;
  132. }
  133. void Install(int signum, THolder<TEventHandler> handler) {
  134. if (DoInstall(signum, std::move(handler))) {
  135. struct sigaction a;
  136. memset(&a, 0, sizeof(a));
  137. a.sa_sigaction = PipeWriterSignalHandler;
  138. a.sa_flags = SA_SIGINFO | SA_RESTART;
  139. Y_ABORT_UNLESS(!sigaction(signum, &a, nullptr), "sigaction failed: %s (errno = %d)", strerror(errno), errno);
  140. }
  141. }
  142. };
  143. // This pointer is never deleted - yeah, it's intended memory leak.
  144. // It is necessary to prevent problems when user's signal handler calls exit function
  145. // which destroys all global variables including this one.
  146. // It such situation we have 2 options:
  147. // - wait for auxiliary thread to die - which will cause dead lock
  148. // - destruct variable, ignoring thread - which will cause data corruption.
  149. std::atomic<TAsyncSignalsHandler*> SIGNALS_HANDLER = nullptr;
  150. }
  151. void SetAsyncSignalHandler(int signum, THolder<TEventHandler> handler) {
  152. static TAdaptiveLock lock;
  153. // Must be in HB with Handler's constructor.
  154. auto* currentHandler = SIGNALS_HANDLER.load(std::memory_order::acquire);
  155. if (Y_UNLIKELY(currentHandler == nullptr)) {
  156. TGuard dnd(lock);
  157. // If we read non-null here it means that we have a concurrent thread
  158. // unlocking the lock establishing strongly HB with us.
  159. // next line is sequenced before lock call thus relaxed is enough here.
  160. currentHandler = SIGNALS_HANDLER.load(std::memory_order::relaxed);
  161. if (currentHandler == nullptr) {
  162. // NEVERS GETS DESTROYED
  163. currentHandler = new TAsyncSignalsHandler();
  164. // Ensure HB with constructor for future readers.
  165. SIGNALS_HANDLER.store(currentHandler, std::memory_order::release);
  166. }
  167. }
  168. currentHandler->Install(signum, std::move(handler));
  169. }
  170. #else //_win_
  171. void SetAsyncSignalHandler(int, THolder<TEventHandler>) {
  172. // TODO: it's really easy to port using _pipe, _read and _write, but it must be tested properly.
  173. }
  174. #endif
  175. namespace {
  176. template <typename TFunc>
  177. class TFunctionEventHandler: public TEventHandler {
  178. TFunc Func;
  179. public:
  180. TFunctionEventHandler(TFunc func) {
  181. if (func)
  182. Func = func;
  183. }
  184. int Handle(int signum) override {
  185. if (Func) {
  186. Func(signum);
  187. }
  188. return 0;
  189. }
  190. };
  191. }
  192. void SetAsyncSignalHandler(int signum, void (*handler)(int)) {
  193. SetAsyncSignalHandler(signum, MakeHolder<TFunctionEventHandler<void (*)(int)>>(handler));
  194. }
  195. void SetAsyncSignalFunction(int signum, std::function<void(int)> func) {
  196. typedef std::function<void(int)> TFunc;
  197. SetAsyncSignalHandler(signum, MakeHolder<TFunctionEventHandler<TFunc>>(func));
  198. }