Branch data Line data Source code
1 : : // Copyright (c) 2022 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 [ # # ]: 0 : SignalInterrupt::SignalInterrupt() : m_flag{false} 19 : : { 20 : : #ifndef WIN32 21 [ # # ]: 0 : std::optional<TokenPipe> pipe = TokenPipe::Make(); 22 [ # # ][ # # ]: 0 : if (!pipe) throw std::ios_base::failure("Could not create TokenPipe"); [ # # ] 23 [ # # ][ # # ]: 0 : m_pipe_r = pipe->TakeReadEnd(); 24 [ # # ][ # # ]: 0 : m_pipe_w = pipe->TakeWriteEnd(); 25 : : #endif 26 : 0 : } 27 : : 28 : 0 : SignalInterrupt::operator bool() const 29 : : { 30 : 0 : return m_flag; 31 : : } 32 : : 33 : 0 : 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 [ # # ][ # # ]: 0 : if (*this && !wait()) return false; 38 : 0 : m_flag = false; 39 : 0 : return true; 40 : 0 : } 41 : : 42 : 0 : 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 [ # # ]: 0 : if (!m_flag.exchange(true)) { 53 : : // Write an arbitrary byte to the write end of the pipe. 54 : 0 : int res = m_pipe_w.TokenWrite('x'); 55 [ # # ]: 0 : if (res != 0) { 56 : 0 : return false; 57 : : } 58 : 0 : } 59 : : #endif 60 : 0 : return true; 61 : 0 : } 62 : : 63 : 0 : 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 : 0 : int res = m_pipe_r.TokenRead(); 70 [ # # ]: 0 : if (res != 'x') { 71 : 0 : return false; 72 : : } 73 : : #endif 74 : 0 : return true; 75 : 0 : } 76 : : 77 : : } // namespace util