util: Add RAII TokenPipe

pull/21007/head
Wladimir J. van der Laan 4 years ago
parent 2e8116149c
commit 612f746a8f

@ -249,6 +249,7 @@ BITCOIN_CORE_H = \
util/system.h \ util/system.h \
util/threadnames.h \ util/threadnames.h \
util/time.h \ util/time.h \
util/tokenpipe.h \
util/trace.h \ util/trace.h \
util/translation.h \ util/translation.h \
util/ui_change_type.h \ util/ui_change_type.h \
@ -577,6 +578,7 @@ libbitcoin_util_a_SOURCES = \
util/strencodings.cpp \ util/strencodings.cpp \
util/string.cpp \ util/string.cpp \
util/time.cpp \ util/time.cpp \
util/tokenpipe.cpp \
$(BITCOIN_CORE_H) $(BITCOIN_CORE_H)
if USE_LIBEVENT if USE_LIBEVENT

@ -0,0 +1,108 @@
// Copyright (c) 2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/tokenpipe.h>
#include <config/bitcoin-config.h>
#ifndef WIN32
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
TokenPipeEnd TokenPipe::TakeReadEnd()
{
TokenPipeEnd res(m_fds[0]);
m_fds[0] = -1;
return res;
}
TokenPipeEnd TokenPipe::TakeWriteEnd()
{
TokenPipeEnd res(m_fds[1]);
m_fds[1] = -1;
return res;
}
TokenPipeEnd::TokenPipeEnd(int fd) : m_fd(fd)
{
}
TokenPipeEnd::~TokenPipeEnd()
{
Close();
}
int TokenPipeEnd::TokenWrite(uint8_t token)
{
while (true) {
ssize_t result = write(m_fd, &token, 1);
if (result < 0) {
// Failure. It's possible that the write was interrupted by a signal,
// in that case retry.
if (errno != EINTR) {
return TS_ERR;
}
} else if (result == 0) {
return TS_EOS;
} else { // ==1
return 0;
}
}
}
int TokenPipeEnd::TokenRead()
{
uint8_t token;
while (true) {
ssize_t result = read(m_fd, &token, 1);
if (result < 0) {
// Failure. Check if the read was interrupted by a signal,
// in that case retry.
if (errno != EINTR) {
return TS_ERR;
}
} else if (result == 0) {
return TS_EOS;
} else { // ==1
return token;
}
}
return token;
}
void TokenPipeEnd::Close()
{
if (m_fd != -1) close(m_fd);
m_fd = -1;
}
std::optional<TokenPipe> TokenPipe::Make()
{
int fds[2] = {-1, -1};
#if HAVE_O_CLOEXEC && HAVE_DECL_PIPE2
if (pipe2(fds, O_CLOEXEC) != 0) {
return std::nullopt;
}
#else
if (pipe(fds) != 0) {
return std::nullopt;
}
#endif
return TokenPipe(fds);
}
TokenPipe::~TokenPipe()
{
Close();
}
void TokenPipe::Close()
{
if (m_fds[0] != -1) close(m_fds[0]);
if (m_fds[1] != -1) close(m_fds[1]);
m_fds[0] = m_fds[1] = -1;
}
#endif // WIN32

@ -0,0 +1,127 @@
// Copyright (c) 2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_TOKENPIPE_H
#define BITCOIN_UTIL_TOKENPIPE_H
#ifndef WIN32
#include <cstdint>
#include <optional>
/** One end of a token pipe. */
class TokenPipeEnd
{
private:
int m_fd = -1;
public:
TokenPipeEnd(int fd = -1);
~TokenPipeEnd();
/** Return value constants for TokenWrite and TokenRead. */
enum Status {
TS_ERR = -1, //!< I/O error
TS_EOS = -2, //!< Unexpected end of stream
};
/** Write token to endpoint.
*
* @returns 0 If successful.
* <0 if error:
* TS_ERR If an error happened.
* TS_EOS If end of stream happened.
*/
int TokenWrite(uint8_t token);
/** Read token from endpoint.
*
* @returns >=0 Token value, if successful.
* <0 if error:
* TS_ERR If an error happened.
* TS_EOS If end of stream happened.
*/
int TokenRead();
/** Explicit close function.
*/
void Close();
/** Return whether endpoint is open.
*/
bool IsOpen() { return m_fd != -1; }
// Move-only class.
TokenPipeEnd(TokenPipeEnd&& other)
{
m_fd = other.m_fd;
other.m_fd = -1;
}
TokenPipeEnd& operator=(TokenPipeEnd&& other)
{
Close();
m_fd = other.m_fd;
other.m_fd = -1;
return *this;
}
TokenPipeEnd(const TokenPipeEnd&) = delete;
TokenPipeEnd& operator=(const TokenPipeEnd&) = delete;
};
/** An interprocess or interthread pipe for sending tokens (one-byte values)
* over.
*/
class TokenPipe
{
private:
int m_fds[2] = {-1, -1};
TokenPipe(int fds[2]) : m_fds{fds[0], fds[1]} {}
public:
~TokenPipe();
/** Create a new pipe.
* @returns The created TokenPipe, or an empty std::nullopt in case of error.
*/
static std::optional<TokenPipe> Make();
/** Take the read end of this pipe. This can only be called once,
* as the object will be moved out.
*/
TokenPipeEnd TakeReadEnd();
/** Take the write end of this pipe. This should only be called once,
* as the object will be moved out.
*/
TokenPipeEnd TakeWriteEnd();
/** Close and end of the pipe that hasn't been moved out.
*/
void Close();
// Move-only class.
TokenPipe(TokenPipe&& other)
{
for (int i = 0; i < 2; ++i) {
m_fds[i] = other.m_fds[i];
other.m_fds[i] = -1;
}
}
TokenPipe& operator=(TokenPipe&& other)
{
Close();
for (int i = 0; i < 2; ++i) {
m_fds[i] = other.m_fds[i];
other.m_fds[i] = -1;
}
return *this;
}
TokenPipe(const TokenPipe&) = delete;
TokenPipe& operator=(const TokenPipe&) = delete;
};
#endif // WIN32
#endif // BITCOIN_UTIL_TOKENPIPE_H
Loading…
Cancel
Save