// SPDX-License-Identifier: LGPL-3.0-or-later // Copyright © 2020-2021 Florian Fischer #include <fcntl.h> #include <sys/eventfd.h> // for eventfd #include <sys/types.h> // for ssize_t #include <array> #include <cassert> // for assert #include <cerrno> // for EBADF, ECANCELED #include <cstdint> // for uint64_t, int32_t #include "Common.hpp" // for DIE_MSG_ERRNO, DIE_MSG #include "Debug.hpp" #include "io.hpp" #include "io/Future.hpp" // for ReadFuture, CloseFuture, WriteFuture using emper::io::CloseFuture; using emper::io::ReadFuture; using emper::io::WriteFuture; static void successChain() { int efd = eventfd(0, 0); if (efd == -1) { DIE_MSG_ERRNO("eventfd failed"); } uint64_t write_buf = 42; WriteFuture writeFuture(efd, &write_buf, sizeof(write_buf), 0); uint64_t read_buf; ReadFuture readFuture(efd, &read_buf, sizeof(read_buf), 0); readFuture.setDependency(writeFuture); CloseFuture closeFuture(efd); closeFuture.setDependency(readFuture); ssize_t res = closeFuture.submitAndWait(); if (res == -1) { DIE_MSG_ERRNO("linked requests chain failed"); } if (read_buf != 42) { DIE_MSG("dependent read value differs from written value"); } } static void successLoop() { int efd = eventfd(0, 0); if (efd == -1) { DIE_MSG_ERRNO("eventfd failed"); } const unsigned ITERATIONS = 10000; for (unsigned i = 0; i < ITERATIONS; ++i) { uint64_t write_buf = 42; WriteFuture writeFuture(efd, &write_buf, sizeof(write_buf), 0); uint64_t read_buf; ReadFuture readFuture(efd, &read_buf, sizeof(read_buf), 0); readFuture.setDependency(writeFuture); ssize_t res = readFuture.submitAndWait(); if (res == -1) { DIE_MSG_ERRNO("linked requests chain failed"); } if (read_buf != 42) { DIE_MSG("dependent read value differs from written value"); } } CloseFuture closeFuture(efd); ssize_t res = closeFuture.submitAndWait(); if (res == -1) { DIE_MSG_ERRNO("linked requests chain failed"); } } static void failureChainInvCor() { std::array<char, 32> buf; // GCC 11.1.0 reports that buf is not initialized. // Which is totally fine because we read into it. IGNORE_MAYBE_UNINITIALIZED ReadFuture invalidReadFuture(-1, buf.data(), buf.size(), 0); POP_DIAGNOSTIC ReadFuture readFuture(0, buf.data(), buf.size(), 0); readFuture.setDependency(invalidReadFuture); int32_t res = readFuture.submitAndWait(); assert(res == -ECANCELED); res = invalidReadFuture.wait(); assert(res == -EBADF); } static void failureChainCorInvCor() { std::array<char, 32> buf; int fd = emper::io::openAndWait("/dev/null", O_WRONLY); // int fd = open("/dev/null", O_WRONLY, 0); assert(fd != -1); WriteFuture correctFuture1(fd, buf.data(), buf.size(), 0); ReadFuture invalidFuture(-1, buf.data(), buf.size(), 0); invalidFuture.setDependency(correctFuture1); WriteFuture correctFuture2(fd, buf.data(), buf.size(), 0); correctFuture2.setDependency(invalidFuture); int32_t res = correctFuture2.submitAndWait(); assert(res == -ECANCELED); res = invalidFuture.wait(); assert(res == -EBADF); res = correctFuture1.wait(); // Since the kernel commit cf10960426515 io_uring does not // submit broken links and completes any request before a // invalid one as canceled. assert(res == -ECANCELED || res == (int32_t)buf.size()); emper::io::closeAndWait(fd); } void emperTest() { // Test if a invalid chain leaves the IoContext in an unexpected / invalid state failureChainCorInvCor(); successChain(); successLoop(); failureChainInvCor(); failureChainCorInvCor(); }