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 201 : SignalInterrupt::operator bool() const 29 : { 30 201 : 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