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 [ + - ]: 1265 : SignalInterrupt::SignalInterrupt() : m_flag{false}
19 : : {
20 : : #ifndef WIN32
21 [ + - ]: 1265 : std::optional<TokenPipe> pipe = TokenPipe::Make();
22 [ - + - - ]: 1265 : if (!pipe) throw std::ios_base::failure("Could not create TokenPipe");
23 [ + - + - ]: 1265 : m_pipe_r = pipe->TakeReadEnd();
24 [ + - + - ]: 1265 : m_pipe_w = pipe->TakeWriteEnd();
25 : : #endif
26 : 1265 : }
27 : :
28 : 1079269 : SignalInterrupt::operator bool() const
29 : : {
30 : 1079269 : 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 : : }
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 : : }
59 : : #endif
60 : : return true;
61 : : }
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 : : return true;
75 : : }
76 : :
77 : : } // namespace util
|