signalinterrupt.cpp raw

   1  // Copyright (c) 2022-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <util/signalinterrupt.h>
   6  
   7  #ifdef WIN32
   8  #include <mutex>
   9  #else
  10  #include <util/tokenpipe.h>
  11  #endif
  12  
  13  #include <ios>
  14  #include <optional>
  15  
  16  namespace util {
  17  
  18  SignalInterrupt::SignalInterrupt() : m_flag{false}
  19  {
  20  #ifndef WIN32
  21      std::optional<TokenPipe> pipe = TokenPipe::Make();
  22      if (!pipe) throw std::ios_base::failure("Could not create TokenPipe");
  23      m_pipe_r = pipe->TakeReadEnd();
  24      m_pipe_w = pipe->TakeWriteEnd();
  25  #endif
  26  }
  27  
  28  SignalInterrupt::operator bool() const
  29  {
  30      return m_flag;
  31  }
  32  
  33  bool SignalInterrupt::reset()
  34  {
  35      // Cancel existing interrupt by waiting for it, this will reset condition flags and remove
  36      // the token from the pipe.
  37      if (*this && !wait()) return false;
  38      m_flag = false;
  39      return true;
  40  }
  41  
  42  bool SignalInterrupt::operator()()
  43  {
  44  #ifdef WIN32
  45      std::unique_lock<std::mutex> lk(m_mutex);
  46      m_flag = true;
  47      m_cv.notify_one();
  48  #else
  49      // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe.
  50      // Make sure that the token is only written once even if multiple threads call this concurrently or in
  51      // case of a reentrant signal.
  52      if (!m_flag.exchange(true)) {
  53          // Write an arbitrary byte to the write end of the pipe.
  54          int res = m_pipe_w.TokenWrite('x');
  55          if (res != 0) {
  56              return false;
  57          }
  58      }
  59  #endif
  60      return true;
  61  }
  62  
  63  bool SignalInterrupt::wait()
  64  {
  65  #ifdef WIN32
  66      std::unique_lock<std::mutex> lk(m_mutex);
  67      m_cv.wait(lk, [this] { return m_flag.load(); });
  68  #else
  69      int res = m_pipe_r.TokenRead();
  70      if (res != 'x') {
  71          return false;
  72      }
  73  #endif
  74      return true;
  75  }
  76  
  77  } // namespace util
  78