diff --git a/emper/io/Future.hpp b/emper/io/Future.hpp index 3cda8c91ad9f13bfa3e0a099c1ed6f1c97a4a01b..463cad7bc90fa2dc82fa2566f9ee5420a5d7a3c9 100644 --- a/emper/io/Future.hpp +++ b/emper/io/Future.hpp @@ -6,6 +6,7 @@ #include <sys/socket.h> // for socklen_t #include <sys/types.h> // for ssize_t +#include <cassert> #include <cstddef> // for size_t #include <cstdint> // for int32_t, uint8_t #include <cstdlib> // for abort @@ -93,6 +94,7 @@ class Future : public Logger<LogSubsystem::IO> { virtual void prepareSqe(io_uring_sqe* sqe) = 0; void setCompletion(int32_t res) { + assert(!state.completed); LOGD("Complete " << this); returnValue = res; state.completed = true; diff --git a/tests/LinkFutureTest.cpp b/tests/LinkFutureTest.cpp index eb7289ba409e9942565882276201dad6f07ee0b8..9a05b0368d8b93f22c982d21b703e5f33ada6fe9 100644 --- a/tests/LinkFutureTest.cpp +++ b/tests/LinkFutureTest.cpp @@ -1,14 +1,17 @@ // 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 <cstdlib> // for exit, EXIT_SUCCESS -#include "Common.hpp" // for DIE_MSG_ERRNO, DIE_MSG +#include "Common.hpp" // for DIE_MSG_ERRNO, DIE_MSG +#include "io.hpp" #include "io/Future.hpp" // for ReadFuture, CloseFuture, WriteFuture using emper::io::CloseFuture; @@ -41,12 +44,45 @@ static void successChain() { } } -static void failureChain() { - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - char buf[32]; - ReadFuture invalidReadFuture(42, &buf, sizeof(buf), 0); +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); - ReadFuture readFuture(0, &buf, sizeof(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; + ReadFuture invalidReadFuture(-1, buf.data(), buf.size(), 0); + + ReadFuture readFuture(0, buf.data(), buf.size(), 0); readFuture.setDependency(invalidReadFuture); int32_t res = readFuture.submitAndWait(); @@ -56,9 +92,46 @@ static void failureChain() { 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(); + assert(res == (int32_t)buf.size()); + + emper::io::close(fd); +} + void emperTest() { + // This leaves the io_uring in a weird state because the successChain() afterwards + // fails because the write future is already completed with -ECANCELED but + // there is successful cqe in the CQ for this future + failureChainCorInvCor(); + successChain(); - failureChain(); + successLoop(); + failureChainInvCor(); + + // failureChainInvCor left the io_uring in a weird state because + // the io_uring_submit of the openAndWait() never returns. + // failureChainCorInvCor(); exit(EXIT_SUCCESS); }