diff options
29 files changed, 323 insertions, 128 deletions
@@ -9,7 +9,7 @@ Tarballs of source and prebuilt binaries for Linux x64 and Windows x64: ### Immediate todos in order of priority: -- [ ] Implement new custom C++ exception synthesis support from Outcome. +- [x] Implement new custom C++ exception synthesis support from Outcome. - [ ] Run clang-tidy fix pass, it's got a bit untidy recently. - [ ] `atomic_append` isn't actually being tested in shared_fs_mutex - [ ] Implement a non-toy ACID key-value BLOB store and send it to Boost for peer review. diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp index ff81a405..73204bcd 100644 --- a/include/afio/revision.hpp +++ b/include/afio/revision.hpp @@ -1,4 +1,4 @@ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define AFIO_PREVIOUS_COMMIT_REF 9e315f74277f3bd9ccf7db0d17e6a991b0afbe46 -#define AFIO_PREVIOUS_COMMIT_DATE "2017-11-20 13:39:42 +00:00" -#define AFIO_PREVIOUS_COMMIT_UNIQUE 9e315f74 +#define AFIO_PREVIOUS_COMMIT_REF e2b51a59d8ae530bdc2af70f785422c084191034 +#define AFIO_PREVIOUS_COMMIT_DATE "2017-11-21 22:22:28 +00:00" +#define AFIO_PREVIOUS_COMMIT_UNIQUE e2b51a59 diff --git a/include/afio/v2.0/algorithm/shared_fs_mutex/lock_files.hpp b/include/afio/v2.0/algorithm/shared_fs_mutex/lock_files.hpp index 713510a3..eb806fd3 100644 --- a/include/afio/v2.0/algorithm/shared_fs_mutex/lock_files.hpp +++ b/include/afio/v2.0/algorithm/shared_fs_mutex/lock_files.hpp @@ -154,7 +154,7 @@ namespace algorithm if(ret.has_error()) { const auto &ec = ret.error(); - if(ec.category() != std::generic_category() || (ec.value() != EAGAIN && ec.value() != EEXIST)) + if(ec != std::errc::resource_unavailable_try_again && ec != std::errc::file_exists) return ret.error(); // Collided with another locker was_contended = n; diff --git a/include/afio/v2.0/config.hpp b/include/afio/v2.0/config.hpp index b1e7430b..3787d9b9 100644 --- a/include/afio/v2.0/config.hpp +++ b/include/afio/v2.0/config.hpp @@ -38,11 +38,18 @@ Distributed under the Boost Software License, Version 1.0. #define AFIO_HEADERS_ONLY 1 #endif +//! \def AFIO_DISABLE_PATHS_IN_FAILURE_INFO +//! \brief Define to not record the current handle's path in any failure info. + #if !defined(AFIO_LOGGING_LEVEL) //! \brief How much detail to log. 0=disabled, 1=fatal, 2=error, 3=warn, 4=info, 5=debug, 6=all. //! Defaults to error level. \ingroup config +#ifdef NDEBUG +#define AFIO_LOGGING_LEVEL 1 // fatal +#else #define AFIO_LOGGING_LEVEL 3 // warn #endif +#endif #ifndef AFIO_LOG_TO_OSTREAM #if !defined(NDEBUG) && !defined(AFIO_DISABLE_LOG_TO_OSTREAM) @@ -300,26 +307,128 @@ AFIO_V2_NAMESPACE_END // Bring in a result implementation #include "outcome/include/outcome.hpp" AFIO_V2_NAMESPACE_BEGIN -// Specialise error_code into this namespace so we can hook result creation via ADL -/*! \struct error_code -\brief Trampoline to `std::error_code`, used to ADL hook `result<T, E>` creation in Outcome. +/*! \struct error_info +\brief The cause of the failure of an operation in AFIO. +*/ +struct error_info +{ + //! The error code for the failure + std::error_code ec; + +#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO +private: + // The id of the thread where this failure occurred + uint32_t _thread_id{0}; + // The TLS path store entry + uint16_t _tls_path_id1{(uint16_t) -1}, _tls_path_id2{(uint16_t) -1}; + // The id of the relevant log entry in the AFIO log (if logging enabled) + size_t _log_id{(size_t) -1}; + +public: +#endif + + //! Default constructor + error_info() = default; + //! Construct from a code and error category + error_info(int code, const std::error_category &cat) + : error_info(std::error_code(code, cat)) + { + } + // Construct from an error code + inline error_info(std::error_code ec); + /* NOTE TO SELF: The error_info constructor implementation is in handle.hpp as we need that + defined before we can do useful logging. + */ + //! Construct from an error condition enum + OUTCOME_TEMPLATE(class ErrorCondEnum) + OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value)) + error_info(ErrorCondEnum &&v) + : error_info(make_error_code(std::forward<ErrorCondEnum>(v))) + { + } + + //! Retrieve any first path associated with this failure. Note this only works if called from the same thread as where the failure occurred. + inline filesystem::path path1() const; + //! Retrieve any second path associated with this failure. Note this only works if called from the same thread as where the failure occurred. + inline filesystem::path path2() const; + //! Retrieve a descriptive message for this failure, possibly with paths and stack backtraces. Extra detail only appears if called from the same thread as where the failure occurred. + inline std::string message() const; + /*! Throw this failure as a C++ exception. Firstly if the error code matches any of the standard + C++ exception types e.g. `bad_alloc`, we throw those types using the string from `message()` + where possible. We then will throw an `error` exception type. + */ + inline void throw_as_exception() const; +}; +inline bool operator==(const error_info &a, const error_info &b) +{ + return a.ec == b.ec; +} +inline bool operator!=(const error_info &a, const error_info &b) +{ + return a.ec != b.ec; +} +#ifndef NDEBUG +// Is trivial in all ways, except default constructibility +static_assert(std::is_trivially_copyable<error_info>::value, "error_info is not a trivially copyable!"); +#endif +inline std::ostream &operator<<(std::ostream &s, const error_info &v) +{ + if(v.ec) + { + return s << "afio::error_info(" << v.message() << ")"; + } + return s << "afio::error_info(null)"; +} +// Tell Outcome that error_info is to be treated as an error_code +inline std::error_code make_error_code(error_info ei) +{ + return ei.ec; +} +// Tell Outcome to call error_info::throw_as_exception() on no-value observation +inline void throw_as_system_error_with_payload(const error_info &ei) +{ + ei.throw_as_exception(); +} + +/*! \class error +\brief The exception type synthesised and thrown when an `afio::result` or `afio::outcome` is no-value observed. */ -struct error_code : public std::error_code +class error : public filesystem::filesystem_error { - // literally passthrough - using std::error_code::error_code; - error_code() = default; - error_code(std::error_code ec) // NOLINT - : std::error_code(ec) +public: + error_info ei; + + //! Constructs from an error_info + explicit error(error_info _ei) + : filesystem::filesystem_error(_ei.message(), _ei.path1(), _ei.path2(), _ei.ec) + , ei(_ei) { } }; -template <class T> using result = OUTCOME_V2_NAMESPACE::result<T, error_code>; -template <class T> using outcome = OUTCOME_V2_NAMESPACE::outcome<T, error_code>; + +inline void error_info::throw_as_exception() const +{ + std::string msg; + try + { + msg = message(); + } + catch(...) + { + } + OUTCOME_V2_NAMESPACE::try_throw_std_exception_from_error(ec, msg); + throw error(*this); +} + +template <class T> using result = OUTCOME_V2_NAMESPACE::result<T, error_info>; +template <class T> using outcome = OUTCOME_V2_NAMESPACE::outcome<T, error_info>; using OUTCOME_V2_NAMESPACE::success; using OUTCOME_V2_NAMESPACE::failure; using OUTCOME_V2_NAMESPACE::error_from_exception; using OUTCOME_V2_NAMESPACE::in_place_type; + +static_assert(OUTCOME_V2_NAMESPACE::trait::has_error_code_v<error_info>, "error_info is not detected to be an error code"); + AFIO_V2_NAMESPACE_END @@ -356,30 +465,18 @@ public: ~log_level_guard() { log().log_level(_v); } }; -/* Some notes on how error logging works: - -Throughout AFIO in almost all of its functions, we call `AFIO_LOG_FUNCTION_CALL(inst)` where `inst` is -usually `this`. We test if inst points to a `handle`, and if so we replace the "current handle" in -TLS storage with the new one, restoring it on stack unwind. - -When an errored result is created, Outcome provides ADL based hooks which let us intercept an errored -result creation. We look up TLS storage for the current handle, and if one is set then we fetch its -current path and store it into a round robin store in TLS, storing the index into the result. Later -on, when we are reporting or otherwise dealing with the error, the path the error refers to can be -printed. - -All this gets compiled out if AFIO_LOGGING_LEVEL < 2 (error) and is skipped if log().log_level() < 2 (error). -In this situation, no paths are captured on error. -*/ -#if AFIO_LOGGING_LEVEL >= 2 +// Infrastructure for recording the current path for when failure occurs +#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO namespace detail { // Our thread local store struct tls_errored_results_t { - handle *current_handle{nullptr}; // The current handle for this thread - bool reentered{false}; // Whether hook_result_construction is reentering itself - char paths[190][16]; // The log can only store 190 chars of message anyway + uint32_t this_thread_id{QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id()}; + handle *current_handle{nullptr}; // The current handle for this thread. Changed via RAII via AFIO_LOG_FUNCTION_CALL, see below. + bool reentering_self{false}; // Prevents any failed call to current_path() by us reentering ourselves + + char paths[190][16]; // Last 190 chars of path uint16_t pathidx{0}; char *next(uint16_t &idx) { @@ -442,6 +539,66 @@ namespace detail #else #define AFIO_LOG_INST_TO_TLS(inst) #endif + +inline filesystem::path error_info::path1() const +{ + if(QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id() == _thread_id) + { + auto &tls = detail::tls_errored_results(); + const char *path1 = tls.get(_tls_path_id1); + if(path1 != nullptr) + { + return filesystem::path(path1); + } + } + return {}; +} +inline filesystem::path error_info::path2() const +{ + if(QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id() == _thread_id) + { + auto &tls = detail::tls_errored_results(); + const char *path2 = tls.get(_tls_path_id2); + if(path2 != nullptr) + { + return filesystem::path(path2); + } + } + return {}; +} +inline std::string error_info::message() const +{ + std::string ret(ec.message()); + if(QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id() == _thread_id) + { + auto &tls = detail::tls_errored_results(); + const char *path1 = tls.get(_tls_path_id1), *path2 = tls.get(_tls_path_id2); + if(path1 != nullptr) + { + ret.append(" [path1 = "); + ret.append(path1); + if(path2 != nullptr) + { + ret.append(", path2 = "); + ret.append(path2); + } + ret.append("]"); + } + } +#if AFIO_LOGGING_LEVEL >= 2 + if(_log_id != (uint32_t) -1) + { + if(log().valid(_log_id)) + { + ret.append(" [location = "); + ret.append(location(log()[_log_id])); + ret.append("]"); + } + } +#endif + return ret; +} + AFIO_V2_NAMESPACE_END #ifndef AFIO_LOG_FATAL_TO_CERR diff --git a/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp b/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp index cf8e92b5..b6cd0ca2 100644 --- a/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp +++ b/include/afio/v2.0/detail/impl/posix/async_file_handle.ipp @@ -88,7 +88,7 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state if(result) { if(errcode) - result = error_code((int) errcode, std::system_category()); + result = error_info((int) errcode, std::system_category()); else { // Figure out which i/o I am and update the buffer in question diff --git a/include/afio/v2.0/detail/impl/posix/io_handle.ipp b/include/afio/v2.0/detail/impl/posix/io_handle.ipp index 07db3578..d38d7b51 100644 --- a/include/afio/v2.0/detail/impl/posix/io_handle.ipp +++ b/include/afio/v2.0/detail/impl/posix/io_handle.ipp @@ -247,7 +247,7 @@ void io_handle::unlock(io_handle::extent_type offset, io_handle::extent_type byt } if(failed) { - error_code ret{errno, std::system_category()}; + error_info ret{errno, std::system_category()}; (void) ret; AFIO_LOG_FATAL(_v.fd, "io_handle::unlock() failed"); std::terminate(); diff --git a/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp b/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp index b99ca114..3cb20f2d 100644 --- a/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp +++ b/include/afio/v2.0/detail/impl/windows/async_file_handle.ipp @@ -54,7 +54,7 @@ template <class BuffersType, class IORoutine> result<async_file_handle::io_state if(result) { if(errcode) - result = error_code{errcode, std::system_category()}; + result = error_info{errcode, std::system_category()}; else { // Figure out which i/o I am and update the buffer in question diff --git a/include/afio/v2.0/detail/impl/windows/fs_handle.ipp b/include/afio/v2.0/detail/impl/windows/fs_handle.ipp index 64d7870d..0341bfd5 100644 --- a/include/afio/v2.0/detail/impl/windows/fs_handle.ipp +++ b/include/afio/v2.0/detail/impl/windows/fs_handle.ipp @@ -182,7 +182,7 @@ result<void> fs_handle::unlink(deadline d) noexcept { // If something else is using it, we may not be able to rename // This error also annoyingly appears if the file has delete on close set on it already - if(out.error().value() == (int) 0xC0000043 /*STATUS_SHARING_VIOLATION*/) + if(out.error().ec.value() == (int) 0xC0000043 /*STATUS_SHARING_VIOLATION*/) { AFIO_LOG_WARN(this, "Failed to rename entry to random name to simulate immediate unlinking due to STATUS_SHARING_VIOLATION, skipping"); } diff --git a/include/afio/v2.0/handle.hpp b/include/afio/v2.0/handle.hpp index 611008e0..7ccc9216 100644 --- a/include/afio/v2.0/handle.hpp +++ b/include/afio/v2.0/handle.hpp @@ -393,49 +393,54 @@ template <class T> struct construct result<T> operator()() const noexcept { static_assert(!std::is_same<T, T>::value, "construct<T>() was not specialised for the type T supplied"); } }; -// Intercept when Outcome creates an errored result and log it to our log -template <class T, class R> inline void hook_result_construction(OUTCOME_V2_NAMESPACE::in_place_type_t<T>, result<R> *res) noexcept +// failure_info is defined in config.hpp, this is its constructor which needs +// to be defined here so that we have handle's definition available +inline error_info::error_info(std::error_code _ec) + : ec(_ec) { - // Sanity check that we never construct a null error code - assert(!res->has_error() || res->error().value() != 0); - if(res->has_error() && log().log_level() >= log_level::error) + // Here is a VERY useful place to breakpoint! + if(ec) { - // Here is a VERY useful place to breakpoint! +#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO auto &tls = detail::tls_errored_results(); - handle *currenth = tls.current_handle; - native_handle_type nativeh; - if(currenth != nullptr) + if(!tls.reentering_self) { - if(tls.reentered) + handle *currenth = tls.current_handle; + native_handle_type nativeh; + if(currenth != nullptr) { - return; - } - tls.reentered = true; - nativeh = currenth->native_handle(); - auto currentpath_ = currenth->current_path(); - if(currentpath_) - { - auto currentpath = currentpath_.value().u8string(); - uint16_t tlsidx = 0; + nativeh = currenth->native_handle(); + // This may fail, if it does it will construct an error_info thus reentering ourselves. Prevent that. + tls.reentering_self = true; + auto currentpath_ = currenth->current_path(); + tls.reentering_self = false; + if(currentpath_) + { + auto currentpath = currentpath_.value().u8string(); + _thread_id = tls.this_thread_id; #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4996) // the function may be unsafe #endif - strncpy(tls.next(tlsidx), QUICKCPPLIB_NAMESPACE::ringbuffer_log::last190(currentpath), 190); + strncpy(tls.next(_tls_path_id1), QUICKCPPLIB_NAMESPACE::ringbuffer_log::last190(currentpath), 190); #ifdef _MSC_VER #pragma warning(pop) #endif - OUTCOME_V2_NAMESPACE::hooks::set_spare_storage(res, tlsidx); + _tls_path_id2 = _tls_path_id1 - 17; // guaranteed invalid + } + } +#endif +#if AFIO_LOGGING_LEVEL >= 2 + if(log().log_level() >= log_level::error) + { + _log_id = log().emplace_back(log_level::error, ec.message().c_str(), (uint32_t) nativeh._init, tls.this_thread_id); } - tls.reentered = false; +#endif } - log().emplace_back(log_level::error, res->assume_error().message().c_str(), (uint32_t) nativeh._init, QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id()); } } -template <class T, class R> inline void hook_result_in_place_construction(OUTCOME_V2_NAMESPACE::in_place_type_t<T> _, result<R> *res) noexcept -{ - hook_result_construction(_, res); -} + +// Define how we log handles and subclasses thereof namespace detail { template <class T> void log_inst_to_info(const handle *inst, const char *buffer) diff --git a/include/afio/v2.0/io_handle.hpp b/include/afio/v2.0/io_handle.hpp index bd012204..49facea7 100644 --- a/include/afio/v2.0/io_handle.hpp +++ b/include/afio/v2.0/io_handle.hpp @@ -79,12 +79,12 @@ public: #ifndef NDEBUG // Is trivial in all ways, except default constructibility static_assert(std::is_trivially_copyable<buffers_type>::value, "buffers_type is not trivially copyable!"); - static_assert(std::is_trivially_assignable<buffers_type, buffers_type>::value, "buffers_type is not trivially assignable!"); - static_assert(std::is_trivially_destructible<buffers_type>::value, "buffers_type is not trivially destructible!"); - static_assert(std::is_trivially_copy_constructible<buffers_type>::value, "buffers_type is not trivially copy constructible!"); - static_assert(std::is_trivially_move_constructible<buffers_type>::value, "buffers_type is not trivially move constructible!"); - static_assert(std::is_trivially_copy_assignable<buffers_type>::value, "buffers_type is not trivially copy assignable!"); - static_assert(std::is_trivially_move_assignable<buffers_type>::value, "buffers_type is not trivially move assignable!"); + // static_assert(std::is_trivially_assignable<buffers_type, buffers_type>::value, "buffers_type is not trivially assignable!"); + // static_assert(std::is_trivially_destructible<buffers_type>::value, "buffers_type is not trivially destructible!"); + // static_assert(std::is_trivially_copy_constructible<buffers_type>::value, "buffers_type is not trivially copy constructible!"); + // static_assert(std::is_trivially_move_constructible<buffers_type>::value, "buffers_type is not trivially move constructible!"); + // static_assert(std::is_trivially_copy_assignable<buffers_type>::value, "buffers_type is not trivially copy assignable!"); + // static_assert(std::is_trivially_move_assignable<buffers_type>::value, "buffers_type is not trivially move assignable!"); static_assert(std::is_standard_layout<buffers_type>::value, "buffers_type is not a standard layout type!"); #endif //! The i/o request type used by this handle. Guaranteed to be `TrivialType` apart from construction, and `StandardLayoutType`. @@ -106,12 +106,12 @@ public: #ifndef NDEBUG // Is trivial in all ways, except default constructibility static_assert(std::is_trivially_copyable<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially copyable!"); - static_assert(std::is_trivially_assignable<io_request<buffers_type>, io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially assignable!"); - static_assert(std::is_trivially_destructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially destructible!"); - static_assert(std::is_trivially_copy_constructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially copy constructible!"); - static_assert(std::is_trivially_move_constructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially move constructible!"); - static_assert(std::is_trivially_copy_assignable<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially copy assignable!"); - static_assert(std::is_trivially_move_assignable<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially move assignable!"); + // static_assert(std::is_trivially_assignable<io_request<buffers_type>, io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially assignable!"); + // static_assert(std::is_trivially_destructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially destructible!"); + // static_assert(std::is_trivially_copy_constructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially copy constructible!"); + // static_assert(std::is_trivially_move_constructible<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially move constructible!"); + // static_assert(std::is_trivially_copy_assignable<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially copy assignable!"); + // static_assert(std::is_trivially_move_assignable<io_request<buffers_type>>::value, "io_request<buffers_type> is not trivially move assignable!"); static_assert(std::is_standard_layout<io_request<buffers_type>>::value, "io_request<buffers_type> is not a standard layout type!"); #endif //! The i/o result type used by this handle. Guaranteed to be `TrivialType` apart from construction.. @@ -153,12 +153,12 @@ public: #ifndef NDEBUG // Is trivial in all ways, except default constructibility static_assert(std::is_trivially_copyable<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially copyable!"); - static_assert(std::is_trivially_assignable<io_result<buffers_type>, io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially assignable!"); - static_assert(std::is_trivially_destructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially destructible!"); - static_assert(std::is_trivially_copy_constructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially copy constructible!"); - static_assert(std::is_trivially_move_constructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially move constructible!"); - static_assert(std::is_trivially_copy_assignable<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially copy assignable!"); - static_assert(std::is_trivially_move_assignable<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially move assignable!"); +// static_assert(std::is_trivially_assignable<io_result<buffers_type>, io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially assignable!"); +// static_assert(std::is_trivially_destructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially destructible!"); +// static_assert(std::is_trivially_copy_constructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially copy constructible!"); +// static_assert(std::is_trivially_move_constructible<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially move constructible!"); +// static_assert(std::is_trivially_copy_assignable<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially copy assignable!"); +// static_assert(std::is_trivially_move_assignable<io_result<buffers_type>>::value, "io_result<buffers_type> is not trivially move assignable!"); //! \todo Why is io_result<buffers_type> not a standard layout type? // static_assert(std::is_standard_layout<result<buffers_type>>::value, "result<buffers_type> is not a standard layout type!"); // static_assert(std::is_standard_layout<io_result<buffers_type>>::value, "io_result<buffers_type> is not a standard layout type!"); diff --git a/include/afio/v2.0/map_handle.hpp b/include/afio/v2.0/map_handle.hpp index d0e15c82..59fcef86 100644 --- a/include/afio/v2.0/map_handle.hpp +++ b/include/afio/v2.0/map_handle.hpp @@ -236,17 +236,14 @@ public: template <class T> using io_result = io_handle::io_result<T>; protected: - section_handle *_section; - char *_addr; - extent_type _offset; - size_type _length; - section_handle::flag _flag; + section_handle *_section{nullptr}; + char *_addr{nullptr}; + extent_type _offset{0}; + size_type _length{0}; + section_handle::flag _flag{section_handle::flag::none}; explicit map_handle(section_handle *section) : _section(section) - , _addr(nullptr) - , _offset(0) - , _length(0) , _flag(section ? section->section_flags() : section_handle::flag::none) { } @@ -255,11 +252,6 @@ public: //! Default constructor constexpr map_handle() : io_handle() - , _section(nullptr) - , _addr(nullptr) - , _offset(0) - , _length(0) - , _flag(section_handle::flag::none) { } AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~map_handle(); diff --git a/include/afio/v2.0/stat.hpp b/include/afio/v2.0/stat.hpp index f1c45668..c82f89d3 100644 --- a/include/afio/v2.0/stat.hpp +++ b/include/afio/v2.0/stat.hpp @@ -135,7 +135,7 @@ struct stat_t { auto v(fill(h, wanted)); if(v.has_error()) - throw std::system_error(v.error()); + v.error().throw_as_exception(); } #endif //! Fills in the structure with metadata, returning number of items filled in diff --git a/include/afio/v2.0/statfs.hpp b/include/afio/v2.0/statfs.hpp index 16c2f312..0027ea7a 100644 --- a/include/afio/v2.0/statfs.hpp +++ b/include/afio/v2.0/statfs.hpp @@ -90,7 +90,7 @@ struct AFIO_DECL statfs_t { auto v(fill(h, wanted)); if(v.has_error()) - throw std::system_error(v.error()); + v.error().throw_as_exception(); } #endif //! Fills in the structure with metadata, returning number of items filled in diff --git a/test/test_kernel_decl.hpp b/test/test_kernel_decl.hpp index fcbb4905..58999aaa 100644 --- a/test/test_kernel_decl.hpp +++ b/test/test_kernel_decl.hpp @@ -34,4 +34,55 @@ Distributed under the Boost Software License, Version 1.0. #define AFIO_TEST_KERNEL_DECL extern QUICKCPPLIB_SYMBOL_EXPORT #endif +#include "kerneltest/include/kerneltest.hpp" + +#if 0 +// Tell KernelTest's outcome how to grok AFIO's result +OUTCOME_V2_NAMESPACE_BEGIN +namespace convert +{ + // Provide custom ValueOrError conversion from afio::result<U> into kerneltest::result<T> + template <class T, class U> struct value_or_error<KERNELTEST_V1_NAMESPACE::result<T>, AFIO_V2_NAMESPACE::result<U>> + { + static constexpr bool enable_result_inputs = true; + static constexpr bool enable_outcome_inputs = true; + + template <class X, // + typename = std::enable_if_t<std::is_same<AFIO_V2_NAMESPACE::result<U>, std::decay_t<X>>::value // + && std::is_constructible<T, U>::value>> // + constexpr KERNELTEST_V1_NAMESPACE::result<T> + operator()(X &&src) + { + // Forward exactly + return src.has_value() ? // + KERNELTEST_V1_NAMESPACE::result<T>{std::forward<X>(src).value()} // + : + KERNELTEST_V1_NAMESPACE::result<T>{make_error_code(std::forward<X>(src).error())}; + } + }; + // Provide custom ValueOrError conversion from afio::result<U> into kerneltest::outcome<T> + template <class T, class U> struct value_or_error<KERNELTEST_V1_NAMESPACE::outcome<T>, AFIO_V2_NAMESPACE::result<U>> + { + static constexpr bool enable_result_inputs = true; + static constexpr bool enable_outcome_inputs = true; + + template <class X, // + typename = std::enable_if_t<std::is_same<AFIO_V2_NAMESPACE::result<U>, std::decay_t<X>>::value // + && std::is_constructible<T, U>::value>> // + constexpr KERNELTEST_V1_NAMESPACE::outcome<T> + operator()(X &&src) + { + // Forward exactly + return src.has_value() ? // + KERNELTEST_V1_NAMESPACE::outcome<T>{std::forward<X>(src).value()} // + : + KERNELTEST_V1_NAMESPACE::outcome<T>{make_error_code(std::forward<X>(src).error())}; + } + }; +} +OUTCOME_V2_NAMESPACE_END +static_assert(std::is_constructible<KERNELTEST_V1_NAMESPACE::result<int>, AFIO_V2_NAMESPACE::result<int>>::value, "kerneltest::result<int> is not constructible from afio::result<int>!"); +static_assert(std::is_constructible<KERNELTEST_V1_NAMESPACE::outcome<int>, AFIO_V2_NAMESPACE::result<int>>::value, "kerneltest::outcome<int> is not constructible from afio::result<int>!"); +#endif + #endif // namespace diff --git a/test/tests/async_io.cpp b/test/tests/async_io.cpp index acf3dc07..1a0609fb 100644 --- a/test/tests/async_io.cpp +++ b/test/tests/async_io.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" #include <future> @@ -67,7 +66,7 @@ static inline void TestAsyncFileHandle() for(auto &i : futures) { afio::async_file_handle::const_buffers_type out = i.first.get(); - //std::cout << out.data()->len << std::endl; + // std::cout << out.data()->len << std::endl; BOOST_CHECK(out.data()->len == 4096); } } diff --git a/test/tests/coroutines.cpp b/test/tests/coroutines.cpp index c5fa3065..b39fc24f 100644 --- a/test/tests/coroutines.cpp +++ b/test/tests/coroutines.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" #include <future> diff --git a/test/tests/current_path.cpp b/test/tests/current_path.cpp index 64d0f3f8..3f2ae559 100644 --- a/test/tests/current_path.cpp +++ b/test/tests/current_path.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" template <class FileHandleType, class DirectoryHandleType> static inline void TestHandleCurrentPath() { diff --git a/test/tests/directory_handle_create_close/kernel_directory_handle.cpp.hpp b/test/tests/directory_handle_create_close/kernel_directory_handle.cpp.hpp index 87d1d5c9..4d064069 100644 --- a/test/tests/directory_handle_create_close/kernel_directory_handle.cpp.hpp +++ b/test/tests/directory_handle_create_close/kernel_directory_handle.cpp.hpp @@ -23,25 +23,24 @@ Distributed under the Boost Software License, Version 1.0. */ #include "../../test_kernel_decl.hpp" -#include "kerneltest/include/kerneltest.hpp" namespace directory_handle_create_close { AFIO_TEST_KERNEL_DECL AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle> test_kernel_directory_handle_absolute(AFIO_V2_NAMESPACE::directory_handle::mode m, AFIO_V2_NAMESPACE::directory_handle::creation c, AFIO_V2_NAMESPACE::directory_handle::flag f, - AFIO_V2_NAMESPACE::directory_handle::buffers_type *entries, KERNELTEST_V1_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info> *info) + AFIO_V2_NAMESPACE::directory_handle::buffers_type *entries, AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info> *info) { auto h = AFIO_V2_NAMESPACE::directory_handle::directory({}, "testdir", m, c, AFIO_V2_NAMESPACE::directory_handle::caching::all, f); if(h) { // git needs a file in a directory to create it, so any directory created needs an empty file called pin.txt (void) AFIO_V2_NAMESPACE::file_handle::file(h.value(), "pin.txt", AFIO_V2_NAMESPACE::file_handle::mode::write, AFIO_V2_NAMESPACE::file_handle::creation::if_needed); - *info = KERNELTEST_V1_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info>(h.value().enumerate(std::move(*entries))); + *info = AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info>(h.value().enumerate(std::move(*entries))); h.value().close().value(); } return h; } AFIO_TEST_KERNEL_DECL AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle> test_kernel_directory_handle_relative(AFIO_V2_NAMESPACE::directory_handle::mode m, AFIO_V2_NAMESPACE::directory_handle::creation c, AFIO_V2_NAMESPACE::directory_handle::flag f, - AFIO_V2_NAMESPACE::directory_handle::buffers_type *entries, KERNELTEST_V1_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info> *info) + AFIO_V2_NAMESPACE::directory_handle::buffers_type *entries, AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info> *info) { OUTCOME_TRY(b, AFIO_V2_NAMESPACE::path_handle::path(".")); auto h = AFIO_V2_NAMESPACE::directory_handle::directory(b, "testdir", m, c, AFIO_V2_NAMESPACE::directory_handle::caching::all, f); @@ -49,7 +48,7 @@ namespace directory_handle_create_close { // git needs a file in a directory to create it, so any directory created needs an empty file called pin.txt (void) AFIO_V2_NAMESPACE::file_handle::file(h.value(), "pin.txt", AFIO_V2_NAMESPACE::file_handle::mode::write, AFIO_V2_NAMESPACE::file_handle::creation::if_needed); - *info = KERNELTEST_V1_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info>(h.value().enumerate(std::move(*entries))); + *info = AFIO_V2_NAMESPACE::result<AFIO_V2_NAMESPACE::directory_handle::enumerate_info>(h.value().enumerate(std::move(*entries))); h.value().close().value(); } b.close().value(); diff --git a/test/tests/directory_handle_create_close/runner.cpp b/test/tests/directory_handle_create_close/runner.cpp index 6b1aa2f4..d3e499c0 100644 --- a/test/tests/directory_handle_create_close/runner.cpp +++ b/test/tests/directory_handle_create_close/runner.cpp @@ -23,11 +23,11 @@ Distributed under the Boost Software License, Version 1.0. */ #include "kernel_directory_handle.cpp.hpp" -#include "kerneltest/include/kerneltest.hpp" template <class U> inline void directory_handle_create_close_creation(U &&f) { using namespace KERNELTEST_V1_NAMESPACE; + using AFIO_V2_NAMESPACE::result; using directory_handle = AFIO_V2_NAMESPACE::directory_handle; static const result<void> no_such_file_or_directory = std::errc::no_such_file_or_directory; static const result<void> file_exists = std::errc::file_exists; diff --git a/test/tests/directory_handle_enumerate/runner.cpp b/test/tests/directory_handle_enumerate/runner.cpp index da99714b..4e28c444 100644 --- a/test/tests/directory_handle_enumerate/runner.cpp +++ b/test/tests/directory_handle_enumerate/runner.cpp @@ -23,11 +23,11 @@ Distributed under the Boost Software License, Version 1.0. */ #include "kernel_directory_handle_enumerate.cpp.hpp" -#include "kerneltest/include/kerneltest.hpp" template <class U> inline void directory_handle_enumerate_(U &&f) { using namespace KERNELTEST_V1_NAMESPACE; + using AFIO_V2_NAMESPACE::result; using AFIO_V2_NAMESPACE::path_view; using AFIO_V2_NAMESPACE::directory_entry; using AFIO_V2_NAMESPACE::directory_handle; diff --git a/test/tests/file_handle_create_close/runner.cpp b/test/tests/file_handle_create_close/runner.cpp index 06360a74..75e4088e 100644 --- a/test/tests/file_handle_create_close/runner.cpp +++ b/test/tests/file_handle_create_close/runner.cpp @@ -22,13 +22,13 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "kerneltest/include/kerneltest.hpp" #include "kernel_async_file_handle.cpp.hpp" #include "kernel_file_handle.cpp.hpp" template <class U> inline void file_handle_create_close_creation(U &&f) { using namespace KERNELTEST_V1_NAMESPACE; + using AFIO_V2_NAMESPACE::result; using file_handle = AFIO_V2_NAMESPACE::file_handle; static const result<void> no_such_file_or_directory = std::errc::no_such_file_or_directory; static const result<void> file_exists = std::errc::file_exists; diff --git a/test/tests/file_handle_lock_unlock.cpp b/test/tests/file_handle_lock_unlock.cpp index 8a1218de..db4d266d 100644 --- a/test/tests/file_handle_lock_unlock.cpp +++ b/test/tests/file_handle_lock_unlock.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" static inline void TestFileHandleLockUnlock() { diff --git a/test/tests/map_handle_create_close/runner.cpp b/test/tests/map_handle_create_close/runner.cpp index fb4c605d..0baedef5 100644 --- a/test/tests/map_handle_create_close/runner.cpp +++ b/test/tests/map_handle_create_close/runner.cpp @@ -23,11 +23,11 @@ Distributed under the Boost Software License, Version 1.0. */ #include "kernel_map_handle.cpp.hpp" -#include "kerneltest/include/kerneltest.hpp" template <class U> inline void map_handle_create_close_(U &&f) { using namespace KERNELTEST_V1_NAMESPACE; + using AFIO_V2_NAMESPACE::result; using AFIO_V2_NAMESPACE::file_handle; using AFIO_V2_NAMESPACE::section_handle; using AFIO_V2_NAMESPACE::map_handle; diff --git a/test/tests/mapped_view.cpp b/test/tests/mapped_view.cpp index 124a5865..3303bb0d 100644 --- a/test/tests/mapped_view.cpp +++ b/test/tests/mapped_view.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" static inline void TestMappedView1() { diff --git a/test/tests/path_discovery.cpp b/test/tests/path_discovery.cpp index bbd76f08..d9f1a150 100644 --- a/test/tests/path_discovery.cpp +++ b/test/tests/path_discovery.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" static inline void TestPathDiscovery() { diff --git a/test/tests/path_view.cpp b/test/tests/path_view.cpp index b860cf33..ccdcba11 100644 --- a/test/tests/path_view.cpp +++ b/test/tests/path_view.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" static inline void TestPathView() { diff --git a/test/tests/section_handle_create_close/runner.cpp b/test/tests/section_handle_create_close/runner.cpp index 4b8ba2ae..507886bb 100644 --- a/test/tests/section_handle_create_close/runner.cpp +++ b/test/tests/section_handle_create_close/runner.cpp @@ -23,11 +23,11 @@ Distributed under the Boost Software License, Version 1.0. */ #include "kernel_section_handle.cpp.hpp" -#include "kerneltest/include/kerneltest.hpp" template <class U> inline void section_handle_create_close_(U &&f) { using namespace KERNELTEST_V1_NAMESPACE; + using AFIO_V2_NAMESPACE::result; using AFIO_V2_NAMESPACE::file_handle; using AFIO_V2_NAMESPACE::section_handle; namespace path_discovery = AFIO_V2_NAMESPACE::path_discovery; diff --git a/test/tests/shared_fs_mutex.cpp b/test/tests/shared_fs_mutex.cpp index 06e029d1..a5cf8cdf 100644 --- a/test/tests/shared_fs_mutex.cpp +++ b/test/tests/shared_fs_mutex.cpp @@ -24,8 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #define QUICKCPPLIB_BOOST_UNIT_TEST_CUSTOM_MAIN_DEFINED -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" KERNELTEST_TEST_KERNEL(unit, afio, shared_fs_mutex, entity_endian, "Tests that afio::algorithm::shared_fs_mutex::entity_type has the right endian", [] { AFIO_V2_NAMESPACE::algorithm::shared_fs_mutex::shared_fs_mutex::entity_type v(0, true); diff --git a/test/tests/trivial_vector.cpp b/test/tests/trivial_vector.cpp index ac263ac3..6c8b542f 100644 --- a/test/tests/trivial_vector.cpp +++ b/test/tests/trivial_vector.cpp @@ -22,8 +22,7 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ -#include "../../include/afio/afio.hpp" -#include "kerneltest/include/kerneltest.hpp" +#include "../test_kernel_decl.hpp" static size_t trivial_vector_udts_constructed; static inline void TestTrivialVector() |