Newer
Older
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2021 Florian Fischer
#include <netinet/in.h>
#include <sys/eventfd.h>
#include <sys/socket.h>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <memory>
#include "Common.hpp"
#include "CountingPrivateSemaphore.hpp"
#include "Debug.hpp"
#include "Future.hpp"
#include "emper.hpp"
#include "fixtures/assert.hpp"
#include "lib/LinuxVersion.hpp"
using emper::io::ReadFuture;
using emper::io::TimeoutWrapper;
static void setupSockPair(int& sock1, int& sock2) {
const int PORT = 4242;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock == -1) DIE_MSG_ERRNO("creating listen socket failed");
int enable = 1;
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == -1)
DIE_MSG_ERRNO("setsockopt SO_REUSEADDR failed");
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)) == -1)
DIE_MSG_ERRNO("setsockopt SO_REUSEPORT failed");
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
if (bind(listen_sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1)
DIE_MSG_ERRNO("bind failed");
if (listen(listen_sock, 1) != 0) DIE_MSG_ERRNO("listen failed");
sock2 = socket(AF_INET, SOCK_STREAM, 0);
if (sock2 == -1) DIE_MSG_ERRNO("creating client socket failed");
CPS cps;
spawn(
[&]() {
if ((sock1 = emper::io::acceptAndWait(listen_sock, nullptr, nullptr)) == -1)
DIE_MSG_ERRNO("accept failed");
},
cps);
if (emper::io::connectAndWait(sock2, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) ==
-1)
DIE_MSG_ERRNO("connect failed");
cps.wait();
emper::io::closeAndForget(listen_sock);
}
void sockTest() {
int sock1, sock2;
DBG("setup sockets");
setupSockPair(sock1, sock2);
uint64_t recvBuf;
DBG("submit recv");
TimeoutWrapper::Timespec ts = {.tv_sec = 1, .tv_nsec = 0};
ssize_t res = emper::io::recvAndTryWait(sock1, &recvBuf, sizeof(recvBuf), 0, ts);
ASSERT(res == -1);
ASSERT(errno == ECANCELED);
// TODO: find a way to test sendAndTryWait
// // allocate a huge buffer which is surely bigger then the sockets buffer and
// // thus causing the send to block triggering the timeout
// const ssize_t MEMB = 1 << 20;
// auto* sendBuf = new char[MEMB];
// DBG("submit send");
// res = emper::io::sendAndTryWait(sock1, &sendBuf, MEMB, 0, ts, true);
// ASSERT(res == -1);
// ASSERT(errno == ECANCELED);
// delete[] sendBuf;
emper::io::closeAndForget(sock1);
emper::io::closeAndForget(sock2);
}
void readTest() {
int efd = eventfd(0, EFD_SEMAPHORE);
if (efd == -1) {
DIE_MSG_ERRNO("eventfd failed");
}
uint64_t readBuf;
TimeoutWrapper::Timespec ts = {.tv_sec = 1, .tv_nsec = 0};
ssize_t res = emper::io::readFileAndTryWait(efd, &readBuf, sizeof(readBuf), ts);
ASSERT(res == -1);
ASSERT(errno == ECANCELED);
emper::io::closeAndForget(efd);
}
void writeTest() {
int efd = eventfd(0, EFD_SEMAPHORE);
if (efd == -1) {
DIE_MSG_ERRNO("eventfd failed");
}
// fill up the eventfd
uint64_t writeBuf = 0xfffffffffffffffe;
if (emper::io::writeFileAndWait(efd, &writeBuf, sizeof(writeBuf)) == -1) {
DIE_MSG("eventfd prep write failed");
}
writeBuf = 1;
TimeoutWrapper::Timespec ts = {.tv_sec = 1, .tv_nsec = 0};
ssize_t res = emper::io::writeFileAndTryWait(efd, &writeBuf, sizeof(writeBuf), ts);
ASSERT(res == -1);
// write requests can't be canceled when in execution so this
// will return as interupted
const int err = errno;
ASSERT(err == EINTR || (EMPER_LINUX_GE("5.16.0") && err == ECANCELED));