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 [ + - ]: 1 : SignalInterrupt::SignalInterrupt() : m_flag{false} 19 : : { 20 : : #ifndef WIN32 21 [ + - ]: 1 : std::optional<TokenPipe> pipe = TokenPipe::Make(); 22 [ + - # # : 1 : if (!pipe) throw std::ios_base::failure("Could not create TokenPipe"); # # ] 23 [ + - + - : 1 : m_pipe_r = pipe->TakeReadEnd(); + - ] 24 [ + - + - : 1 : m_pipe_w = pipe->TakeWriteEnd(); + - ] 25 : : #endif 26 : 1 : } 27 : : 28 : 1 : SignalInterrupt::operator bool() const 29 : : { 30 : 1 : return m_flag; 31 : : } 32 : : 33 : 0 : void 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(); 38 : 0 : m_flag = false; 39 : 0 : } 40 : : 41 : 0 : void SignalInterrupt::operator()() 42 : : { 43 : : #ifdef WIN32 44 : : std::unique_lock<std::mutex> lk(m_mutex); 45 : : m_flag = true; 46 : : m_cv.notify_one(); 47 : : #else 48 : : // This must be reentrant and safe for calling in a signal handler, so using a condition variable is not safe. 49 : : // Make sure that the token is only written once even if multiple threads call this concurrently or in 50 : : // case of a reentrant signal. 51 [ # # ]: 0 : if (!m_flag.exchange(true)) { 52 : : // Write an arbitrary byte to the write end of the pipe. 53 : 0 : int res = m_pipe_w.TokenWrite('x'); 54 [ # # ]: 0 : if (res != 0) { 55 [ # # ]: 0 : throw std::ios_base::failure("Could not write interrupt token"); 56 : : } 57 : 0 : } 58 : : #endif 59 : 0 : } 60 : : 61 : 0 : void SignalInterrupt::wait() 62 : : { 63 : : #ifdef WIN32 64 : : std::unique_lock<std::mutex> lk(m_mutex); 65 : : m_cv.wait(lk, [this] { return m_flag.load(); }); 66 : : #else 67 : 0 : int res = m_pipe_r.TokenRead(); 68 [ # # ]: 0 : if (res != 'x') { 69 [ # # ]: 0 : throw std::ios_base::failure("Did not read expected interrupt token"); 70 : : } 71 : : #endif 72 : 0 : } 73 : : 74 : : } // namespace util