void setSocketNoDelay(int socket) {
int flag = 1;
socklen_t optlen = sizeof(flag);
- SYSCHECK(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, optlen));
+ SYSCHECK_ERR_RETURN_NEG1(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, optlen));
}
PortType getSocketPort(int fd) {
PortType listenPort;
struct ::sockaddr_storage addrStorage;
socklen_t addrLen = sizeof(addrStorage);
- SYSCHECK(getsockname(
+ SYSCHECK_ERR_RETURN_NEG1(getsockname(
fd, reinterpret_cast<struct ::sockaddr*>(&addrStorage), &addrLen));
if (addrStorage.ss_family == AF_INET) {
char address[INET6_ADDRSTRLEN + 1];
if (addr->sa_family == AF_INET) {
struct ::sockaddr_in* s = reinterpret_cast<struct ::sockaddr_in*>(addr);
- SYSCHECK(::inet_ntop(AF_INET, &(s->sin_addr), address, INET_ADDRSTRLEN))
+ SYSCHECK(::inet_ntop(AF_INET, &(s->sin_addr), address, INET_ADDRSTRLEN), __output != nullptr)
address[INET_ADDRSTRLEN] = '\0';
} else if (addr->sa_family == AF_INET6) {
struct ::sockaddr_in6* s = reinterpret_cast<struct ::sockaddr_in6*>(addr);
- SYSCHECK(::inet_ntop(AF_INET6, &(s->sin6_addr), address, INET6_ADDRSTRLEN))
+ SYSCHECK(::inet_ntop(AF_INET6, &(s->sin6_addr), address, INET6_ADDRSTRLEN), __output != nullptr)
address[INET6_ADDRSTRLEN] = '\0';
} else {
throw std::runtime_error("unsupported protocol");
int socket;
while (true) {
try {
- SYSCHECK(
+ SYSCHECK_ERR_RETURN_NEG1(
socket = ::socket(
nextAddr->ai_family,
nextAddr->ai_socktype,
nextAddr->ai_protocol))
int optval = 1;
- SYSCHECK(
+ SYSCHECK_ERR_RETURN_NEG1(
::setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)))
- SYSCHECK(::bind(socket, nextAddr->ai_addr, nextAddr->ai_addrlen))
- SYSCHECK(::listen(socket, LISTEN_QUEUE_SIZE))
+ SYSCHECK_ERR_RETURN_NEG1(::bind(socket, nextAddr->ai_addr, nextAddr->ai_addrlen))
+ SYSCHECK_ERR_RETURN_NEG1(::listen(socket, LISTEN_QUEUE_SIZE))
break;
} catch (const std::system_error& e) {
bool anyRefused = false;
while (true) {
try {
- SYSCHECK(
+ SYSCHECK_ERR_RETURN_NEG1(
socket = ::socket(
nextAddr->ai_family,
nextAddr->ai_socktype,
ResourceGuard socketGuard([socket]() { ::close(socket); });
// We need to connect in non-blocking mode, so we can use a timeout
- SYSCHECK(::fcntl(socket, F_SETFL, O_NONBLOCK));
+ SYSCHECK_ERR_RETURN_NEG1(::fcntl(socket, F_SETFL, O_NONBLOCK));
int ret = ::connect(socket, nextAddr->ai_addr, nextAddr->ai_addrlen);
// Disable non-blocking mode
int flags;
- SYSCHECK(flags = ::fcntl(socket, F_GETFL));
- SYSCHECK(::fcntl(socket, F_SETFL, flags & (~O_NONBLOCK)));
+ SYSCHECK_ERR_RETURN_NEG1(flags = ::fcntl(socket, F_GETFL));
+ SYSCHECK_ERR_RETURN_NEG1(::fcntl(socket, F_SETFL, flags & (~O_NONBLOCK)));
socketGuard.release();
break;
}
int socket;
- SYSCHECK(socket = ::accept(listenSocket, NULL, NULL))
+ SYSCHECK_ERR_RETURN_NEG1(socket = ::accept(listenSocket, NULL, NULL))
// Get address of the connecting process
struct ::sockaddr_storage addr;
socklen_t addrLen = sizeof(addr);
- SYSCHECK(::getpeername(
+ SYSCHECK_ERR_RETURN_NEG1(::getpeername(
socket, reinterpret_cast<struct ::sockaddr*>(&addr), &addrLen))
setSocketNoDelay(socket);
using PortType = uint16_t;
using SizeType = uint64_t;
-#define SYSCHECK(expr) \
- { \
- do { \
- errno = 0; \
- auto ___output = (expr); \
- (void)___output; \
- } while (errno == EINTR); \
- if (errno != 0) \
+// `errno` is only meaningful when it fails. E.g., a successful `fork()` sets
+// `errno` to `EINVAL` in child process on some macos
+// (https://stackoverflow.com/a/20295079), and thus `errno` should really only
+// be inspected if an error occured.
+//
+// `success_cond` is an expression used to check if an error has happend. So for
+// `fork()`, we can use `SYSCHECK(pid = fork(), pid != -1)`. The function output
+// is stored in variable `__output` and may be used in `success_cond`.
+#define SYSCHECK(expr, success_cond) \
+while (true) { \
+ auto __output = (expr); \
+ (void) __output; \
+ if (!(success_cond)) { \
+ if (errno == EINTR) { \
+ continue; \
+ } else { \
throw std::system_error(errno, std::system_category()); \
- }
+ } \
+ } else { \
+ break; \
+ } \
+}
+
+// Most functions indicate error by returning `-1`. This is a helper macro for
+// this common case with `SYSCHECK`.
+#define SYSCHECK_ERR_RETURN_NEG1(expr) SYSCHECK(expr, __output != -1)
// Helper resource guard class
class ResourceGuard {
while (bytesToSend > 0) {
ssize_t bytesSent;
- SYSCHECK(bytesSent = ::send(socket, currentBytes, bytesToSend, flags))
+ SYSCHECK_ERR_RETURN_NEG1(bytesSent = ::send(socket, currentBytes, bytesToSend, flags))
if (bytesSent == 0) {
throw std::system_error(ECONNRESET, std::system_category());
}
while (bytesToReceive > 0) {
ssize_t bytesReceived;
- SYSCHECK(bytesReceived = ::recv(socket, currentBytes, bytesToReceive, 0))
+ SYSCHECK_ERR_RETURN_NEG1(bytesReceived = ::recv(socket, currentBytes, bytesToReceive, 0))
if (bytesReceived == 0) {
throw std::system_error(ECONNRESET, std::system_category());
}