Skip to content

Commit 612f746

Browse files
committed
util: Add RAII TokenPipe
1 parent 2e81161 commit 612f746

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ BITCOIN_CORE_H = \
249249
util/system.h \
250250
util/threadnames.h \
251251
util/time.h \
252+
util/tokenpipe.h \
252253
util/trace.h \
253254
util/translation.h \
254255
util/ui_change_type.h \
@@ -577,6 +578,7 @@ libbitcoin_util_a_SOURCES = \
577578
util/strencodings.cpp \
578579
util/string.cpp \
579580
util/time.cpp \
581+
util/tokenpipe.cpp \
580582
$(BITCOIN_CORE_H)
581583

582584
if USE_LIBEVENT

src/util/tokenpipe.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) 2021 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+
#include <util/tokenpipe.h>
5+
6+
#include <config/bitcoin-config.h>
7+
8+
#ifndef WIN32
9+
10+
#include <errno.h>
11+
#include <fcntl.h>
12+
#include <unistd.h>
13+
14+
TokenPipeEnd TokenPipe::TakeReadEnd()
15+
{
16+
TokenPipeEnd res(m_fds[0]);
17+
m_fds[0] = -1;
18+
return res;
19+
}
20+
21+
TokenPipeEnd TokenPipe::TakeWriteEnd()
22+
{
23+
TokenPipeEnd res(m_fds[1]);
24+
m_fds[1] = -1;
25+
return res;
26+
}
27+
28+
TokenPipeEnd::TokenPipeEnd(int fd) : m_fd(fd)
29+
{
30+
}
31+
32+
TokenPipeEnd::~TokenPipeEnd()
33+
{
34+
Close();
35+
}
36+
37+
int TokenPipeEnd::TokenWrite(uint8_t token)
38+
{
39+
while (true) {
40+
ssize_t result = write(m_fd, &token, 1);
41+
if (result < 0) {
42+
// Failure. It's possible that the write was interrupted by a signal,
43+
// in that case retry.
44+
if (errno != EINTR) {
45+
return TS_ERR;
46+
}
47+
} else if (result == 0) {
48+
return TS_EOS;
49+
} else { // ==1
50+
return 0;
51+
}
52+
}
53+
}
54+
55+
int TokenPipeEnd::TokenRead()
56+
{
57+
uint8_t token;
58+
while (true) {
59+
ssize_t result = read(m_fd, &token, 1);
60+
if (result < 0) {
61+
// Failure. Check if the read was interrupted by a signal,
62+
// in that case retry.
63+
if (errno != EINTR) {
64+
return TS_ERR;
65+
}
66+
} else if (result == 0) {
67+
return TS_EOS;
68+
} else { // ==1
69+
return token;
70+
}
71+
}
72+
return token;
73+
}
74+
75+
void TokenPipeEnd::Close()
76+
{
77+
if (m_fd != -1) close(m_fd);
78+
m_fd = -1;
79+
}
80+
81+
std::optional<TokenPipe> TokenPipe::Make()
82+
{
83+
int fds[2] = {-1, -1};
84+
#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2
85+
if (pipe2(fds, O_CLOEXEC) != 0) {
86+
return std::nullopt;
87+
}
88+
#else
89+
if (pipe(fds) != 0) {
90+
return std::nullopt;
91+
}
92+
#endif
93+
return TokenPipe(fds);
94+
}
95+
96+
TokenPipe::~TokenPipe()
97+
{
98+
Close();
99+
}
100+
101+
void TokenPipe::Close()
102+
{
103+
if (m_fds[0] != -1) close(m_fds[0]);
104+
if (m_fds[1] != -1) close(m_fds[1]);
105+
m_fds[0] = m_fds[1] = -1;
106+
}
107+
108+
#endif // WIN32

src/util/tokenpipe.h

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright (c) 2021 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+
#ifndef BITCOIN_UTIL_TOKENPIPE_H
6+
#define BITCOIN_UTIL_TOKENPIPE_H
7+
8+
#ifndef WIN32
9+
10+
#include <cstdint>
11+
#include <optional>
12+
13+
/** One end of a token pipe. */
14+
class TokenPipeEnd
15+
{
16+
private:
17+
int m_fd = -1;
18+
19+
public:
20+
TokenPipeEnd(int fd = -1);
21+
~TokenPipeEnd();
22+
23+
/** Return value constants for TokenWrite and TokenRead. */
24+
enum Status {
25+
TS_ERR = -1, //!< I/O error
26+
TS_EOS = -2, //!< Unexpected end of stream
27+
};
28+
29+
/** Write token to endpoint.
30+
*
31+
* @returns 0 If successful.
32+
* <0 if error:
33+
* TS_ERR If an error happened.
34+
* TS_EOS If end of stream happened.
35+
*/
36+
int TokenWrite(uint8_t token);
37+
38+
/** Read token from endpoint.
39+
*
40+
* @returns >=0 Token value, if successful.
41+
* <0 if error:
42+
* TS_ERR If an error happened.
43+
* TS_EOS If end of stream happened.
44+
*/
45+
int TokenRead();
46+
47+
/** Explicit close function.
48+
*/
49+
void Close();
50+
51+
/** Return whether endpoint is open.
52+
*/
53+
bool IsOpen() { return m_fd != -1; }
54+
55+
// Move-only class.
56+
TokenPipeEnd(TokenPipeEnd&& other)
57+
{
58+
m_fd = other.m_fd;
59+
other.m_fd = -1;
60+
}
61+
TokenPipeEnd& operator=(TokenPipeEnd&& other)
62+
{
63+
Close();
64+
m_fd = other.m_fd;
65+
other.m_fd = -1;
66+
return *this;
67+
}
68+
TokenPipeEnd(const TokenPipeEnd&) = delete;
69+
TokenPipeEnd& operator=(const TokenPipeEnd&) = delete;
70+
};
71+
72+
/** An interprocess or interthread pipe for sending tokens (one-byte values)
73+
* over.
74+
*/
75+
class TokenPipe
76+
{
77+
private:
78+
int m_fds[2] = {-1, -1};
79+
80+
TokenPipe(int fds[2]) : m_fds{fds[0], fds[1]} {}
81+
82+
public:
83+
~TokenPipe();
84+
85+
/** Create a new pipe.
86+
* @returns The created TokenPipe, or an empty std::nullopt in case of error.
87+
*/
88+
static std::optional<TokenPipe> Make();
89+
90+
/** Take the read end of this pipe. This can only be called once,
91+
* as the object will be moved out.
92+
*/
93+
TokenPipeEnd TakeReadEnd();
94+
95+
/** Take the write end of this pipe. This should only be called once,
96+
* as the object will be moved out.
97+
*/
98+
TokenPipeEnd TakeWriteEnd();
99+
100+
/** Close and end of the pipe that hasn't been moved out.
101+
*/
102+
void Close();
103+
104+
// Move-only class.
105+
TokenPipe(TokenPipe&& other)
106+
{
107+
for (int i = 0; i < 2; ++i) {
108+
m_fds[i] = other.m_fds[i];
109+
other.m_fds[i] = -1;
110+
}
111+
}
112+
TokenPipe& operator=(TokenPipe&& other)
113+
{
114+
Close();
115+
for (int i = 0; i < 2; ++i) {
116+
m_fds[i] = other.m_fds[i];
117+
other.m_fds[i] = -1;
118+
}
119+
return *this;
120+
}
121+
TokenPipe(const TokenPipe&) = delete;
122+
TokenPipe& operator=(const TokenPipe&) = delete;
123+
};
124+
125+
#endif // WIN32
126+
127+
#endif // BITCOIN_UTIL_TOKENPIPE_H

0 commit comments

Comments
 (0)