diff --git a/emper/c_emper.cpp b/emper/c_emper.cpp index 85a9bf9605939c90edc2bd9c3f49c0db4aa61586..4a648a33b40ae464e694b49ae8eb2e834c904839 100644 --- a/emper/c_emper.cpp +++ b/emper/c_emper.cpp @@ -9,9 +9,11 @@ #include "emper.h" // for fiber, cps, bps, runtime #ifdef EMPER_IO +#include <fcntl.h> #include <sys/socket.h> // for socklen_t #include <sys/types.h> // for off_t +#include <cstdarg> #include <cstddef> // for size_t #include "io.hpp" // for emper::io::emper_* @@ -139,5 +141,33 @@ auto emper_writev(int fildes, const struct iovec* iov, int iovcnt) -> ssize_t { return emper::io::writevAndWait(fildes, iov, iovcnt); } +// C function overloading inspired by +// http://locklessinc.com/articles/overloading +auto emper_open(const char* pathname, int flags, ...) -> int { + if (flags | O_CREAT || flags | O_TMPFILE) { + va_list v; + va_start(v, flags); + + mode_t mode = va_arg(v, mode_t); + va_end(v); + return emper::io::openAndWait(pathname, flags, mode); + } + + return emper::io::openAndWait(pathname, flags); +} + +auto emper_openat(int dirfd, const char* pathname, int flags, ...) -> int { + if (flags | O_CREAT || flags | O_TMPFILE) { + va_list v; + va_start(v, flags); + + mode_t mode = va_arg(v, mode_t); + va_end(v); + return emper::io::openatAndWait(dirfd, pathname, flags, mode); + } + + return emper::io::openatAndWait(dirfd, pathname, flags); +} + auto emper_close(int fd) -> int { return emper::io::closeAndWait(fd); } #endif diff --git a/emper/include/emper.h b/emper/include/emper.h index 4a1402e7013f856c6808d1c3db84f23d9a48e04e..e21ad5a94aa3d24737e4ecc64338f094f7375cc3 100644 --- a/emper/include/emper.h +++ b/emper/include/emper.h @@ -2,12 +2,14 @@ // Copyright © 2020 Florian Schmaus, Florian Fischer #pragma once +#include <stdarg.h> // NOLINT(modernize-deprecated-headers) +#include <stddef.h> // NOLINT(modernize-deprecated-headers) + #include "emper-common.h" #include "emper-config.h" -#include "stddef.h" // NOLINT(modernize-deprecated-headers) #ifdef EMPER_IO -#include "sys/socket.h" +#include <sys/socket.h> #endif typedef struct runtime runtime; // NOLINT(modernize-use-using) @@ -88,6 +90,12 @@ ssize_t emper_write_file(int fildes, const void* buf, size_t nbyte, off_t offset // NOLINTNEXTLINE(modernize-use-trailing-return-type) ssize_t emper_writev(int fildes, const struct iovec* iov, int iovcnt); +// NOLINTNEXTLINE(modernize-use-trailing-return-type) +int emper_open(const char* pathname, int flags, ...); + +// NOLINTNEXTLINE(modernize-use-trailing-return-type) +int emper_openat(int dirfd, const char* pathname, int flags, ...); + // NOLINTNEXTLINE(modernize-use-trailing-return-type) int emper_close(int fd); #endif diff --git a/emper/io.hpp b/emper/io.hpp index fc154f36359f0e5689162c16a3099ca7db72e26c..4addc610e9c80b0b7132736ae78a1471ef85cd61 100644 --- a/emper/io.hpp +++ b/emper/io.hpp @@ -2,6 +2,7 @@ // Copyright © 2020-2021 Florian Fischer #pragma once +#include <fcntl.h> #include <sys/socket.h> // for socklen_t #include <sys/types.h> // for ssize_t, off_t @@ -334,6 +335,81 @@ inline auto writevAndWait(int fildes, const struct iovec *iov, int iovcnt) -> ss return future.waitAndSetErrno(); } +/** + * @brief asynchronous openat mimicking POSIX openat(3) + * + * This method must be called from inside the emper runtime because it uses + * the worker-local IoContext + * + * @param dirfd directory the pathname is interpreted relative to + * @param pathname path to the file to open + * @param flags specify access mode + * @param mode in which the file should be created + * + * @return Future object which signals the completion of the openat request + */ +inline auto openat(int dirfd, const char *pathname, int flags, mode_t mode = 0) + -> std::unique_ptr<Future> { + auto future = std::make_unique<OpenatFuture>(dirfd, pathname, flags, mode); + future->submit(); + return future; +} + +/** + * @brief synchronous openat mimicking POSIX openat(3) + * + * This method must be called from inside the emper runtime because it uses + * the worker-local IoContext + * + * @param dirfd directory the pathname is interpreted relative to + * @param pathname path to the file to open + * @param flags specify access mode + * @param mode in which the file should be created + * + * @return fd on success, -1 on error + */ +inline auto openatAndWait(int dirfd, const char *pathname, int flags, mode_t mode = 0) -> size_t { + OpenatFuture future(dirfd, pathname, flags, mode); + future.submit(); + return future.waitAndSetErrno(); +} + +/** + * @brief asynchronous open mimicking POSIX open(3) + * + * This method must be called from inside the emper runtime because it uses + * the worker-local IoContext + * + * @param pathname path to the file to open + * @param flags specify access mode + * @param mode in which the file should be created + * + * @return Future object which signals the completion of the open request + */ +inline auto open(const char *pathname, int flags, mode_t mode = 0) -> std::unique_ptr<Future> { + auto future = std::make_unique<OpenatFuture>(AT_FDCWD, pathname, flags, mode); + future->submit(); + return future; +} + +/** + * @brief synchronous open mimicking POSIX open(3) + * + * This method must be called from inside the emper runtime because it uses + * the worker-local IoContext + * + * @param pathname path to the file to open + * @param flags specify access mode + * @param mode in which the file should be created + * + * @return fd on success, -1 on error + */ +inline auto openAndWait(const char *pathname, int flags, mode_t mode = 0) -> size_t { + OpenatFuture future(AT_FDCWD, pathname, flags, mode); + future.submit(); + return future.waitAndSetErrno(); +} + /** * @brief Non-blocking close mimicking POSIX close(3) * diff --git a/emper/io/Future.hpp b/emper/io/Future.hpp index 772cd74ea707f402b2631f95d97b37d3c296b7f0..bbf29d5d481cc0add4b88f195a9889ca7cedffa9 100644 --- a/emper/io/Future.hpp +++ b/emper/io/Future.hpp @@ -421,6 +421,16 @@ class ReadFuture : public PartialCompletableFuture { read_all ? ENABLE_PARTIAL_COMPLETION : DISABLE_PARTIAL_COMPLETION){}; }; +class OpenatFuture : public Future { + void prepareSqe(io_uring_sqe* sqe) override { + io_uring_prep_openat(sqe, fd, reinterpret_cast<const char*>(buf), len, offsetOrFlags); + } + + public: + OpenatFuture(int dirfd, const void* pathname, int flags, mode_t mode = 0) + : Future(Operation::OPENAT, dirfd, const_cast<void*>(pathname), flags, mode){}; +}; + class WriteFuture : public PartialCompletableFuture { void prepareSqe(io_uring_sqe* sqe) override { if (partialCompletion == DISABLE_PARTIAL_COMPLETION) { diff --git a/emper/io/Operation.cpp b/emper/io/Operation.cpp index 83a67abef68c6c3648f05008b120dbb8734ad087..e881f1b1ab7cb5a39d03317d5b0f5e028e85f876 100644 --- a/emper/io/Operation.cpp +++ b/emper/io/Operation.cpp @@ -20,6 +20,9 @@ auto operator<<(std::ostream& os, const Operation& op) -> std::ostream& { case Operation::ACCEPT: os << "accept"; break; + case Operation::OPENAT: + os << "openat"; + break; case Operation::READ: os << "read"; break; diff --git a/emper/io/Operation.hpp b/emper/io/Operation.hpp index 498919293214983e1174e86aedb315e63361dbe4..d193b1cf4c5b0a357e8716eaaaa5d511c692e6b3 100644 --- a/emper/io/Operation.hpp +++ b/emper/io/Operation.hpp @@ -11,6 +11,7 @@ enum class Operation { RECV, CONNECT, ACCEPT, + OPENAT, READ, WRITE, WRITEV, diff --git a/tests/SimpleDiskAndNetworkTest.cpp b/tests/SimpleDiskAndNetworkTest.cpp index cf5ec5036c7a698769eac274d98abc5c2cf396e1..60a2e569580ac658e8ad878fe4d4393f2039e102 100644 --- a/tests/SimpleDiskAndNetworkTest.cpp +++ b/tests/SimpleDiskAndNetworkTest.cpp @@ -63,7 +63,7 @@ static void server_func(int sockfd) { } close(file_fd); - file_fd = open(file_name, O_RDONLY); + file_fd = emper::io::openAndWait(file_name, O_RDONLY); if (file_fd == -1) { DIE_MSG_ERRNO("open failed"); } @@ -113,7 +113,7 @@ static void server_func(int sockfd) { } close(file_fd); - file_fd = open(file2_name, O_RDONLY); + file_fd = emper::io::openAndWait(file2_name, O_RDONLY); if (file_fd == -1) { DIE_MSG_ERRNO("open failed"); }