Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-06-24 19:29:12 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-06-24 19:29:12 +0300
commit4547cc6a15ddb63e0dbe337d166ed9a817c65a92 (patch)
tree41357672515636f80f74dd4ecdcbbdcf724c53fe
parent7b50437109eab2007b6e3cd2b41bb6b0cdd49488 (diff)
Broke logging and status_code out into their own headers.
-rw-r--r--cmake/headers.cmake2
-rw-r--r--include/afio/revision.hpp6
-rw-r--r--include/afio/v2.0/config.hpp753
-rw-r--r--include/afio/v2.0/handle.hpp1
-rw-r--r--include/afio/v2.0/logging.hpp334
-rw-r--r--include/afio/v2.0/status_code.hpp464
6 files changed, 805 insertions, 755 deletions
diff --git a/cmake/headers.cmake b/cmake/headers.cmake
index a5137105..9199c39f 100644
--- a/cmake/headers.cmake
+++ b/cmake/headers.cmake
@@ -26,6 +26,7 @@ set(afio_HEADERS
"include/afio/v2.0/handle.hpp"
"include/afio/v2.0/io_handle.hpp"
"include/afio/v2.0/io_service.hpp"
+ "include/afio/v2.0/logging.hpp"
"include/afio/v2.0/map_handle.hpp"
"include/afio/v2.0/mapped_file_handle.hpp"
"include/afio/v2.0/native_handle_type.hpp"
@@ -34,6 +35,7 @@ set(afio_HEADERS
"include/afio/v2.0/path_view.hpp"
"include/afio/v2.0/stat.hpp"
"include/afio/v2.0/statfs.hpp"
+ "include/afio/v2.0/status_code.hpp"
"include/afio/v2.0/storage_profile.hpp"
"include/afio/v2.0/utils.hpp"
"include/afio/version.hpp"
diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp
index e9b61413..215c0b0c 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 c95d49034e7c24eda5c6d3dea04fcd7463ff8457
-#define AFIO_PREVIOUS_COMMIT_DATE "2018-06-22 17:56:38 +00:00"
-#define AFIO_PREVIOUS_COMMIT_UNIQUE c95d4903
+#define AFIO_PREVIOUS_COMMIT_REF 7b50437109eab2007b6e3cd2b41bb6b0cdd49488
+#define AFIO_PREVIOUS_COMMIT_DATE "2018-06-23 23:19:32 +00:00"
+#define AFIO_PREVIOUS_COMMIT_UNIQUE 7b504371
diff --git a/include/afio/v2.0/config.hpp b/include/afio/v2.0/config.hpp
index afd3c636..c6f08c9d 100644
--- a/include/afio/v2.0/config.hpp
+++ b/include/afio/v2.0/config.hpp
@@ -253,6 +253,7 @@ struct path_hasher
size_t operator()(const filesystem::path &p) const { return std::hash<filesystem::path::string_type>()(p.native()); }
};
AFIO_V2_NAMESPACE_END
+#include <ctime> // for struct timespec
// Configure AFIO_DECL
@@ -359,758 +360,6 @@ namespace detail
AFIO_V2_NAMESPACE_END
-/* The SG14 status code implementation is quite profoundly different to the
-error code implementation. In the error code implementation, std::error_code
-is fixed by the standard library, so we wrap it with extra metadata into
-an error_info type. error_info then constructs off a code and a code domain
-tag.
-
-Status code, on the other hand, is templated and is designed for custom
-domains which can set arbitrary payloads. So we define custom domains and
-status codes for AFIO with these combinations:
-
-- win32_error{ DWORD }
-- ntkernel_error{ LONG }
-- posix_error{ int }
-- generic_error{ errc }
-
-Each of these is a separate AFIO custom status code domain. We also define
-an erased form of these custom domains, and that is typedefed to
-error_domain<intptr_t>::value_type.
-
-This design ensure that AFIO can be configured into either std-based error
-handling or SG14 experimental status code handling. It defaults to the latter
-as that (a) enables safe header only AFIO on Windows (b) produces better codegen
-(c) drags in far fewer STL headers.
-*/
-
-#if AFIO_EXPERIMENTAL_STATUS_CODE
-
-// Bring in a result implementation based on status_code
-#include "outcome/include/outcome/experimental/status_result.hpp"
-#include "outcome/include/outcome/try.hpp"
-
-AFIO_V2_NAMESPACE_BEGIN
-
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
-
-namespace detail
-{
- template <class T> struct error_domain_value_type
- {
- //! \brief The type of code
- T sc{};
-
- // The id of the thread where this failure occurred
- uint32_t _thread_id{0};
- // The TLS path store entry
- uint16_t _tls_path_id1{static_cast<uint16_t>(-1)}, _tls_path_id2{static_cast<uint16_t>(-1)};
- // The id of the relevant log entry in the AFIO log (if logging enabled)
- size_t _log_id{static_cast<size_t>(-1)};
-
- //! Default construction
- error_domain_value_type() = default;
-
- //! Implicitly constructs an instance
- constexpr inline error_domain_value_type(T _sc)
- : sc(_sc)
- {
- } // NOLINT
-
- //! Compares to a T
- constexpr bool operator==(const T &b) const noexcept { return sc == b; }
- };
-}
-
-/*! \class error_domain
-\brief The SG14 status code domain for errors in AFIO.
-*/
-template <class BaseStatusCodeDomain> class error_domain : public BaseStatusCodeDomain
-{
- friend class SYSTEM_ERROR2_NAMESPACE::status_code<error_domain>;
- using _base = BaseStatusCodeDomain;
-
-public:
- using string_ref = typename BaseStatusCodeDomain::string_ref;
- using atomic_refcounted_string_ref = typename BaseStatusCodeDomain::atomic_refcounted_string_ref;
-
- //! \brief The value type of errors in AFIO
- using value_type = detail::error_domain_value_type<typename _base::value_type>;
-
- error_domain() = default;
- error_domain(const error_domain &) = default;
- error_domain(error_domain &&) = default;
- error_domain &operator=(const error_domain &) = default;
- error_domain &operator=(error_domain &&) = default;
- ~error_domain() = default;
-
-protected:
- virtual inline string_ref _do_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final;
-};
-#else
-template <class BaseStatusCodeDomain> using error_domain = BaseStatusCodeDomain;
-#endif
-
-namespace detail
-{
- using error_domain_value_system_code = error_domain_value_type<SYSTEM_ERROR2_NAMESPACE::system_code::value_type>;
-}
-
-//! An erased status code
-using error_code = SYSTEM_ERROR2_NAMESPACE::status_code<SYSTEM_ERROR2_NAMESPACE::erased<detail::error_domain_value_system_code>>;
-
-
-template <class T> using result = OUTCOME_V2_NAMESPACE::experimental::erased_result<T, error_code>;
-using OUTCOME_V2_NAMESPACE::success;
-using OUTCOME_V2_NAMESPACE::failure;
-using OUTCOME_V2_NAMESPACE::in_place_type;
-
-//! Choose an errc implementation
-using SYSTEM_ERROR2_NAMESPACE::errc;
-
-//! Helper for constructing an error code from an errc
-inline error_code generic_error(errc c);
-#ifndef _WIN32
-//! Helper for constructing an error code from a POSIX errno
-inline error_code posix_error(int c = errno);
-#else
-//! Helper for constructing an error code from a DWORD
-inline error_code win32_error(SYSTEM_ERROR2_NAMESPACE::win32::DWORD c = SYSTEM_ERROR2_NAMESPACE::win32::GetLastError());
-//! Helper for constructing an error code from a NTSTATUS
-inline error_code ntkernel_error(SYSTEM_ERROR2_NAMESPACE::win32::NTSTATUS c);
-#endif
-
-namespace detail
-{
- inline std::ostream &operator<<(std::ostream &s, const error_code &v) { return s << "afio::error_code(" << v.message().c_str() << ")"; }
-}
-inline error_code error_from_exception(std::exception_ptr &&ep = std::current_exception(), error_code not_matched = generic_error(errc::resource_unavailable_try_again)) noexcept
-{
- if(!ep)
- {
- return generic_error(errc::success);
- }
- try
- {
- std::rethrow_exception(ep);
- }
- catch(const std::invalid_argument & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::invalid_argument);
- }
- catch(const std::domain_error & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::argument_out_of_domain);
- }
- catch(const std::length_error & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::argument_list_too_long);
- }
- catch(const std::out_of_range & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::result_out_of_range);
- }
- catch(const std::logic_error & /*unused*/) /* base class for this group */
- {
- ep = std::exception_ptr();
- return generic_error(errc::invalid_argument);
- }
- catch(const std::system_error &e) /* also catches ios::failure */
- {
- ep = std::exception_ptr();
- if(e.code().category() == std::generic_category())
- {
- return generic_error(static_cast<errc>(static_cast<int>(e.code().value())));
- }
- // Don't know this error code category, so fall through
- }
- catch(const std::overflow_error & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::value_too_large);
- }
- catch(const std::range_error & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::result_out_of_range);
- }
- catch(const std::runtime_error & /*unused*/) /* base class for this group */
- {
- ep = std::exception_ptr();
- return generic_error(errc::resource_unavailable_try_again);
- }
- catch(const std::bad_alloc & /*unused*/)
- {
- ep = std::exception_ptr();
- return generic_error(errc::not_enough_memory);
- }
- catch(...)
- {
- }
- return not_matched;
-}
-
-AFIO_V2_NAMESPACE_END
-
-
-#else // AFIO_EXPERIMENTAL_STATUS_CODE
-
-
-// Bring in a result implementation based on std::error_code
-#include "outcome/include/outcome.hpp"
-
-AFIO_V2_NAMESPACE_BEGIN
-
-namespace detail
-{
- template <class Src> inline void append_path_info(Src &src, std::string &ret);
- template <class Dest, class Src> inline void fill_failure_info(Dest &dest, const Src &src);
-}
-
-struct error_info;
-inline std::error_code make_error_code(error_info ei);
-
-/*! \struct error_info
-\brief The cause of the failure of an operation in AFIO.
-*/
-struct error_info
-{
- friend inline std::error_code make_error_code(error_info ei);
- template <class Src> friend inline void detail::append_path_info(Src &src, std::string &ret);
- template <class Dest, class Src> friend inline void detail::fill_failure_info(Dest &dest, const Src &src);
-
-private:
- // The error code for the failure
- std::error_code ec;
-
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
- // The id of the thread where this failure occurred
- uint32_t _thread_id{0};
- // The TLS path store entry
- uint16_t _tls_path_id1{static_cast<uint16_t>(-1)}, _tls_path_id2{static_cast<uint16_t>(-1)};
- // The id of the relevant log entry in the AFIO log (if logging enabled)
- size_t _log_id{static_cast<size_t>(-1)};
-
-public:
-#endif
-
- //! Default constructor
- error_info() = default;
- // Explicit construction from an error code
- explicit inline error_info(std::error_code _ec); // NOLINT
- /* NOTE TO SELF: The error_info constructor implementation is in handle.hpp as we need that
- defined before we can do useful logging.
- */
- //! Implicit 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) // NOLINT
- : error_info(make_error_code(std::forward<ErrorCondEnum>(v)))
- {
- }
-
- //! Retrieve the value of the error code
- int value() const noexcept { return ec.value(); }
- //! 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_exception() const;
-};
-inline bool operator==(const error_info &a, const error_info &b)
-{
- return make_error_code(a) == make_error_code(b);
-}
-inline bool operator!=(const error_info &a, const error_info &b)
-{
- return make_error_code(a) != make_error_code(b);
-}
-OUTCOME_TEMPLATE(class ErrorCondEnum)
-OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
-inline bool operator==(const error_info &a, const ErrorCondEnum &b)
-{
- return make_error_code(a) == std::error_condition(b);
-}
-OUTCOME_TEMPLATE(class ErrorCondEnum)
-OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
-inline bool operator==(const ErrorCondEnum &a, const error_info &b)
-{
- return std::error_condition(a) == make_error_code(b);
-}
-OUTCOME_TEMPLATE(class ErrorCondEnum)
-OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
-inline bool operator!=(const error_info &a, const ErrorCondEnum &b)
-{
- return make_error_code(a) != std::error_condition(b);
-}
-OUTCOME_TEMPLATE(class ErrorCondEnum)
-OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
-inline bool operator!=(const ErrorCondEnum &a, const error_info &b)
-{
- return std::error_condition(a) != make_error_code(b);
-}
-#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(make_error_code(v))
- {
- 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_exception() on no-value observation
-inline void outcome_throw_as_system_error_with_payload(const error_info &ei)
-{
- ei.throw_exception();
-}
-
-/*! \class error
-\brief The exception type synthesised and thrown when an `afio::result` or `afio::outcome` is no-value observed.
-*/
-class error : public filesystem::filesystem_error
-{
-public:
- error_info ei;
-
- //! Constructs from an error_info
- explicit error(error_info _ei)
- : filesystem::filesystem_error(_ei.message(), _ei.path1(), _ei.path2(), make_error_code(_ei))
- , ei(_ei)
- {
- }
-};
-
-inline void error_info::throw_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;
-inline error_info error_from_exception(std::exception_ptr &&ep = std::current_exception(), std::error_code not_matched = std::make_error_code(std::errc::resource_unavailable_try_again)) noexcept
-{
- return error_info(OUTCOME_V2_NAMESPACE::error_from_exception(std::move(ep), not_matched));
-}
-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");
-
-//! Choose an errc implementation
-using std::errc;
-
-//! Helper for constructing an error info from an errc
-inline error_info generic_error(errc c)
-{
- return error_info(make_error_code(c));
-}
-#ifndef _WIN32
-//! Helper for constructing an error info from a POSIX errno
-inline error_info posix_error(int c = errno)
-{
- return error_info(std::error_code(c, std::system_category()));
-}
-#endif
-
-AFIO_V2_NAMESPACE_END
-#endif // AFIO_EXPERIMENTAL_STATUS_CODE
-
-
-#if AFIO_LOGGING_LEVEL
-
-/*! \todo TODO FIXME Replace in-memory log with memory map file backed log.
-*/
-AFIO_V2_NAMESPACE_BEGIN
-
-//! The log used by AFIO
-inline AFIO_DECL QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<AFIO_LOGGING_MEMORY> &log() noexcept
-{
- static QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<AFIO_LOGGING_MEMORY> _log(static_cast<QUICKCPPLIB_NAMESPACE::ringbuffer_log::level>(AFIO_LOGGING_LEVEL));
-#ifdef AFIO_LOG_TO_OSTREAM
- if(_log.immediate() != &AFIO_LOG_TO_OSTREAM)
- {
- _log.immediate(&AFIO_LOG_TO_OSTREAM);
- }
-#endif
- return _log;
-}
-//! Enum for the log level
-using log_level = QUICKCPPLIB_NAMESPACE::ringbuffer_log::level;
-//! RAII class for temporarily adjusting the log level
-class log_level_guard
-{
- log_level _v;
-
-public:
- log_level_guard() = delete;
- log_level_guard(const log_level_guard &) = delete;
- log_level_guard(log_level_guard &&) = delete;
- log_level_guard &operator=(const log_level_guard &) = delete;
- log_level_guard &operator=(log_level_guard &&) = delete;
- explicit log_level_guard(log_level n)
- : _v(log().log_level())
- {
- log().log_level(n);
- }
- ~log_level_guard() { log().log_level(_v); }
-};
-
-// 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
- {
- 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)
- {
- idx = pathidx++;
- return paths[idx % 16]; // NOLINT
- }
- const char *get(uint16_t idx) const
- {
- // If the idx is stale, return not found
- if(idx - pathidx >= 16)
- {
- return nullptr;
- }
- return paths[idx % 16]; // NOLINT
- }
- };
- inline tls_errored_results_t &tls_errored_results()
- {
-#if AFIO_THREAD_LOCAL_IS_CXX11
- static thread_local tls_errored_results_t v;
- return v;
-#else
- static AFIO_THREAD_LOCAL tls_errored_results_t *v;
- if(!v)
- {
- v = new tls_errored_results_t;
- }
- return *v;
-#endif
- }
- template <bool _enabled> struct tls_current_handle_holder
- {
- handle *old{nullptr};
- bool enabled{false};
- tls_current_handle_holder() = delete;
- tls_current_handle_holder(const tls_current_handle_holder &) = delete;
- tls_current_handle_holder(tls_current_handle_holder &&) = delete;
- tls_current_handle_holder &operator=(const tls_current_handle_holder &) = delete;
- tls_current_handle_holder &operator=(tls_current_handle_holder &&) = delete;
- explicit tls_current_handle_holder(const handle *h)
- {
- if(h != nullptr && log().log_level() >= log_level::error)
- {
- auto &tls = tls_errored_results();
- old = tls.current_handle;
- tls.current_handle = const_cast<handle *>(h); // NOLINT
- enabled = true;
- }
- }
- ~tls_current_handle_holder()
- {
- if(enabled)
- {
- auto &tls = tls_errored_results();
- tls.current_handle = old;
- }
- }
- };
- template <> struct tls_current_handle_holder<false>
- {
- tls_current_handle_holder() = delete;
- tls_current_handle_holder(const tls_current_handle_holder &) = delete;
- tls_current_handle_holder(tls_current_handle_holder &&) = delete;
- tls_current_handle_holder &operator=(const tls_current_handle_holder &) = delete;
- tls_current_handle_holder &operator=(tls_current_handle_holder &&) = delete;
- ~tls_current_handle_holder() = default;
- template <class T> explicit tls_current_handle_holder(T && /*unused*/) {}
- };
-#define AFIO_LOG_INST_TO_TLS(inst) AFIO_V2_NAMESPACE::detail::tls_current_handle_holder<std::is_base_of<AFIO_V2_NAMESPACE::handle, std::decay_t<std::remove_pointer_t<decltype(inst)>>>::value> AFIO_UNIQUE_NAME(inst)
-} // namespace detail
-#else // AFIO_DISABLE_PATHS_IN_FAILURE_INFO
-#define AFIO_LOG_INST_TO_TLS(inst)
-#endif // AFIO_DISABLE_PATHS_IN_FAILURE_INFO
-
-AFIO_V2_NAMESPACE_END
-
-#ifndef AFIO_LOG_FATAL_TO_CERR
-#include <cstdio>
-#define AFIO_LOG_FATAL_TO_CERR(expr) \
- fprintf(stderr, "%s\n", (expr)); \
- fflush(stderr)
-#endif
-#endif
-
-#if AFIO_LOGGING_LEVEL >= 1
-#define AFIO_LOG_FATAL(inst, message) \
- { \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::fatal, (message), AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 1U)) ? nullptr : __func__, __LINE__); \
- AFIO_LOG_FATAL_TO_CERR(message); \
- }
-#else
-#define AFIO_LOG_FATAL(inst, message) AFIO_LOG_FATAL_TO_CERR(message)
-#endif
-#if AFIO_LOGGING_LEVEL >= 2
-#define AFIO_LOG_ERROR(inst, message) \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::error, (message), AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 2U)) ? nullptr : __func__, __LINE__)
-#else
-#define AFIO_LOG_ERROR(inst, message)
-#endif
-#if AFIO_LOGGING_LEVEL >= 3
-#define AFIO_LOG_WARN(inst, message) \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::warn, (message), AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 3U)) ? nullptr : __func__, __LINE__)
-#else
-#define AFIO_LOG_WARN(inst, message)
-#endif
-#if AFIO_LOGGING_LEVEL >= 4
-#define AFIO_LOG_INFO(inst, message) \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::info, (message), AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 4U)) ? nullptr : __func__, __LINE__)
-
-// Need to expand out our namespace into a string
-#define AFIO_LOG_STRINGIFY9(s) #s "::"
-#define AFIO_LOG_STRINGIFY8(s) AFIO_LOG_STRINGIFY9(s)
-#define AFIO_LOG_STRINGIFY7(s) AFIO_LOG_STRINGIFY8(s)
-#define AFIO_LOG_STRINGIFY6(s) AFIO_LOG_STRINGIFY7(s)
-#define AFIO_LOG_STRINGIFY5(s) AFIO_LOG_STRINGIFY6(s)
-#define AFIO_LOG_STRINGIFY4(s) AFIO_LOG_STRINGIFY5(s)
-#define AFIO_LOG_STRINGIFY3(s) AFIO_LOG_STRINGIFY4(s)
-#define AFIO_LOG_STRINGIFY2(s) AFIO_LOG_STRINGIFY3(s)
-#define AFIO_LOG_STRINGIFY(s) AFIO_LOG_STRINGIFY2(s)
-AFIO_V2_NAMESPACE_BEGIN
-namespace detail
-{
- // Returns the AFIO namespace as a string
- inline span<char> afio_namespace_string()
- {
- static char buffer[64];
- static size_t length;
- if(length)
- return span<char>(buffer, length);
- const char *src = AFIO_LOG_STRINGIFY(AFIO_V2_NAMESPACE);
- char *bufferp = buffer;
- for(; *src && (bufferp - buffer) < (ptrdiff_t) sizeof(buffer); src++)
- {
- if(*src != ' ')
- *bufferp++ = *src;
- }
- *bufferp = 0;
- length = bufferp - buffer;
- return span<char>(buffer, length);
- }
- // Returns the Outcome namespace as a string
- inline span<char> outcome_namespace_string()
- {
- static char buffer[64];
- static size_t length;
- if(length)
- return span<char>(buffer, length);
- const char *src = AFIO_LOG_STRINGIFY(OUTCOME_V2_NAMESPACE);
- char *bufferp = buffer;
- for(; *src && (bufferp - buffer) < (ptrdiff_t) sizeof(buffer); src++)
- {
- if(*src != ' ')
- *bufferp++ = *src;
- }
- *bufferp = 0;
- length = bufferp - buffer;
- return span<char>(buffer, length);
- }
- // Strips a __PRETTY_FUNCTION__ of all instances of AFIO_V2_NAMESPACE:: and AFIO_V2_NAMESPACE::
- inline void strip_pretty_function(char *out, size_t bytes, const char *in)
- {
- const span<char> remove1 = afio_namespace_string();
- const span<char> remove2 = outcome_namespace_string();
- for(--bytes; bytes && *in; --bytes)
- {
- if(!strncmp(in, remove1.data(), remove1.size()))
- in += remove1.size();
- if(!strncmp(in, remove2.data(), remove2.size()))
- in += remove2.size();
- *out++ = *in++;
- }
- *out = 0;
- }
- template <class T> void log_inst_to_info(T &&inst, const char *buffer) { AFIO_LOG_INFO(inst, buffer); }
-}
-AFIO_V2_NAMESPACE_END
-#ifdef _MSC_VER
-#define AFIO_LOG_FUNCTION_CALL(inst) \
- if(log().log_level() >= log_level::info) \
- { \
- char buffer[256]; \
- AFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __FUNCSIG__); \
- AFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \
- } \
- AFIO_LOG_INST_TO_TLS(inst)
-#else
-#define AFIO_LOG_FUNCTION_CALL(inst) \
- if(log().log_level() >= log_level::info) \
- { \
- char buffer[256]; \
- AFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __PRETTY_FUNCTION__); \
- AFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \
- } \
- AFIO_LOG_INST_TO_TLS(inst)
-#endif
-#else
-#define AFIO_LOG_INFO(inst, message)
-#define AFIO_LOG_FUNCTION_CALL(inst) AFIO_LOG_INST_TO_TLS(inst)
-#endif
-#if AFIO_LOGGING_LEVEL >= 5
-#define AFIO_LOG_DEBUG(inst, message) \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::debug, AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 5U)) ? nullptr : __func__, __LINE__)
-#else
-#define AFIO_LOG_DEBUG(inst, message)
-#endif
-#if AFIO_LOGGING_LEVEL >= 6
-#define AFIO_LOG_ALL(inst, message) \
- AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::all, (message), AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 6U)) ? nullptr : __func__, __LINE__)
-#else
-#define AFIO_LOG_ALL(inst, message)
-#endif
-
-AFIO_V2_NAMESPACE_BEGIN
-
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
-namespace detail
-{
- template <class Src> inline void append_path_info(Src &src, std::string &ret)
- {
- if(QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id() == src._thread_id)
- {
- auto &tls = detail::tls_errored_results();
- const char *path1 = tls.get(src._tls_path_id1), *path2 = tls.get(src._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(src._log_id != static_cast<uint32_t>(-1))
- {
- if(log().valid(src._log_id))
- {
- ret.append(" [location = ");
- ret.append(location(log()[src._log_id]));
- ret.append("]");
- }
- }
-#endif
- }
-}
-#endif
-
-#if AFIO_EXPERIMENTAL_STATUS_CODE
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
-template <class BaseStatusCodeDomain> inline typename error_domain<BaseStatusCodeDomain>::string_ref error_domain<BaseStatusCodeDomain>::_do_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept // NOLINT
-{
- assert(code.domain() == *this);
- const auto &v = static_cast<const SYSTEM_ERROR2_NAMESPACE::status_code<error_domain> &>(code); // NOLINT
- std::string ret = _base::_do_message(code).c_str();
- detail::append_path_info(v.value(), ret);
- char *p = (char *) malloc(ret.size() + 1);
- if(p == nullptr)
- {
- return string_ref("Failed to allocate memory to store error string");
- }
- memcpy(p, ret.c_str(), ret.size() + 1);
- return atomic_refcounted_string_ref(p, ret.size());
-}
-#endif
-
-#else // AFIO_EXPERIMENTAL_STATUS_CODE
-
-inline filesystem::path error_info::path1() const
-{
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
- 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);
- }
- }
-#endif
- return {};
-}
-inline filesystem::path error_info::path2() const
-{
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
- 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);
- }
- }
-#endif
- return {};
-}
-inline std::string error_info::message() const
-{
- std::string ret(ec.message());
-#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
- detail::append_path_info(*this, ret);
-#endif
- return ret;
-}
-
-#endif // AFIO_EXPERIMENTAL_STATUS_CODE
-
-AFIO_V2_NAMESPACE_END
-
-
-#include <ctime> // for struct timespec
-
AFIO_V2_NAMESPACE_BEGIN
namespace detail
diff --git a/include/afio/v2.0/handle.hpp b/include/afio/v2.0/handle.hpp
index f51c3fd2..57f7618f 100644
--- a/include/afio/v2.0/handle.hpp
+++ b/include/afio/v2.0/handle.hpp
@@ -27,6 +27,7 @@ Distributed under the Boost Software License, Version 1.0.
#include "deadline.h"
#include "native_handle_type.hpp"
+#include "status_code.hpp"
#include <algorithm> // for std::count
#include <cassert>
diff --git a/include/afio/v2.0/logging.hpp b/include/afio/v2.0/logging.hpp
new file mode 100644
index 00000000..851678b7
--- /dev/null
+++ b/include/afio/v2.0/logging.hpp
@@ -0,0 +1,334 @@
+/* Configures AFIO
+(C) 2015-2018 Niall Douglas <http://www.nedproductions.biz/> (24 commits)
+File Created: Dec 2015
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License in the accompanying file
+Licence.txt or at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file Licence.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#ifndef AFIO_LOGGING_HPP
+#define AFIO_LOGGING_HPP
+
+#include "config.hpp"
+
+#if AFIO_LOGGING_LEVEL
+
+/*! \todo TODO FIXME Replace in-memory log with memory map file backed log.
+*/
+AFIO_V2_NAMESPACE_BEGIN
+
+//! The log used by AFIO
+inline AFIO_DECL QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<AFIO_LOGGING_MEMORY> &log() noexcept
+{
+ static QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<AFIO_LOGGING_MEMORY> _log(static_cast<QUICKCPPLIB_NAMESPACE::ringbuffer_log::level>(AFIO_LOGGING_LEVEL));
+#ifdef AFIO_LOG_TO_OSTREAM
+ if(_log.immediate() != &AFIO_LOG_TO_OSTREAM)
+ {
+ _log.immediate(&AFIO_LOG_TO_OSTREAM);
+ }
+#endif
+ return _log;
+}
+//! Enum for the log level
+using log_level = QUICKCPPLIB_NAMESPACE::ringbuffer_log::level;
+//! RAII class for temporarily adjusting the log level
+class log_level_guard
+{
+ log_level _v;
+
+public:
+ log_level_guard() = delete;
+ log_level_guard(const log_level_guard &) = delete;
+ log_level_guard(log_level_guard &&) = delete;
+ log_level_guard &operator=(const log_level_guard &) = delete;
+ log_level_guard &operator=(log_level_guard &&) = delete;
+ explicit log_level_guard(log_level n)
+ : _v(log().log_level())
+ {
+ log().log_level(n);
+ }
+ ~log_level_guard() { log().log_level(_v); }
+};
+
+// 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
+ {
+ 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)
+ {
+ idx = pathidx++;
+ return paths[idx % 16]; // NOLINT
+ }
+ const char *get(uint16_t idx) const
+ {
+ // If the idx is stale, return not found
+ if(idx - pathidx >= 16)
+ {
+ return nullptr;
+ }
+ return paths[idx % 16]; // NOLINT
+ }
+ };
+ inline tls_errored_results_t &tls_errored_results()
+ {
+#if AFIO_THREAD_LOCAL_IS_CXX11
+ static thread_local tls_errored_results_t v;
+ return v;
+#else
+ static AFIO_THREAD_LOCAL tls_errored_results_t *v;
+ if(!v)
+ {
+ v = new tls_errored_results_t;
+ }
+ return *v;
+#endif
+ }
+ template <bool _enabled> struct tls_current_handle_holder
+ {
+ handle *old{nullptr};
+ bool enabled{false};
+ tls_current_handle_holder() = delete;
+ tls_current_handle_holder(const tls_current_handle_holder &) = delete;
+ tls_current_handle_holder(tls_current_handle_holder &&) = delete;
+ tls_current_handle_holder &operator=(const tls_current_handle_holder &) = delete;
+ tls_current_handle_holder &operator=(tls_current_handle_holder &&) = delete;
+ explicit tls_current_handle_holder(const handle *h)
+ {
+ if(h != nullptr && log().log_level() >= log_level::error)
+ {
+ auto &tls = tls_errored_results();
+ old = tls.current_handle;
+ tls.current_handle = const_cast<handle *>(h); // NOLINT
+ enabled = true;
+ }
+ }
+ ~tls_current_handle_holder()
+ {
+ if(enabled)
+ {
+ auto &tls = tls_errored_results();
+ tls.current_handle = old;
+ }
+ }
+ };
+ template <> struct tls_current_handle_holder<false>
+ {
+ tls_current_handle_holder() = delete;
+ tls_current_handle_holder(const tls_current_handle_holder &) = delete;
+ tls_current_handle_holder(tls_current_handle_holder &&) = delete;
+ tls_current_handle_holder &operator=(const tls_current_handle_holder &) = delete;
+ tls_current_handle_holder &operator=(tls_current_handle_holder &&) = delete;
+ ~tls_current_handle_holder() = default;
+ template <class T> explicit tls_current_handle_holder(T && /*unused*/) {}
+ };
+#define AFIO_LOG_INST_TO_TLS(inst) ::AFIO_V2_NAMESPACE::detail::tls_current_handle_holder<std::is_base_of<::AFIO_V2_NAMESPACE::handle, std::decay_t<std::remove_pointer_t<decltype(inst)>>>::value> AFIO_UNIQUE_NAME(inst)
+} // namespace detail
+#else // AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+#define AFIO_LOG_INST_TO_TLS(inst)
+#endif // AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+
+AFIO_V2_NAMESPACE_END
+
+#ifndef AFIO_LOG_FATAL_TO_CERR
+#include <cstdio>
+#define AFIO_LOG_FATAL_TO_CERR(expr) \
+ fprintf(stderr, "%s\n", (expr)); \
+ fflush(stderr)
+#endif
+#endif // AFIO_LOGGING_LEVEL
+
+#if AFIO_LOGGING_LEVEL >= 1
+#define AFIO_LOG_FATAL(inst, message) \
+ { \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::fatal, (message), ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 1U)) ? nullptr : __func__, __LINE__); \
+ AFIO_LOG_FATAL_TO_CERR(message); \
+ }
+#else
+#define AFIO_LOG_FATAL(inst, message) AFIO_LOG_FATAL_TO_CERR(message)
+#endif
+#if AFIO_LOGGING_LEVEL >= 2
+#define AFIO_LOG_ERROR(inst, message) \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::error, (message), ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 2U)) ? nullptr : __func__, __LINE__)
+#else
+#define AFIO_LOG_ERROR(inst, message)
+#endif
+#if AFIO_LOGGING_LEVEL >= 3
+#define AFIO_LOG_WARN(inst, message) \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::warn, (message), ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 3U)) ? nullptr : __func__, __LINE__)
+#else
+#define AFIO_LOG_WARN(inst, message)
+#endif
+#if AFIO_LOGGING_LEVEL >= 4
+#define AFIO_LOG_INFO(inst, message) \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::info, (message), ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 4U)) ? nullptr : __func__, __LINE__)
+
+// Need to expand out our namespace into a string
+#define AFIO_LOG_STRINGIFY9(s) #s "::"
+#define AFIO_LOG_STRINGIFY8(s) AFIO_LOG_STRINGIFY9(s)
+#define AFIO_LOG_STRINGIFY7(s) AFIO_LOG_STRINGIFY8(s)
+#define AFIO_LOG_STRINGIFY6(s) AFIO_LOG_STRINGIFY7(s)
+#define AFIO_LOG_STRINGIFY5(s) AFIO_LOG_STRINGIFY6(s)
+#define AFIO_LOG_STRINGIFY4(s) AFIO_LOG_STRINGIFY5(s)
+#define AFIO_LOG_STRINGIFY3(s) AFIO_LOG_STRINGIFY4(s)
+#define AFIO_LOG_STRINGIFY2(s) AFIO_LOG_STRINGIFY3(s)
+#define AFIO_LOG_STRINGIFY(s) AFIO_LOG_STRINGIFY2(s)
+AFIO_V2_NAMESPACE_BEGIN
+namespace detail
+{
+ // Returns the AFIO namespace as a string
+ inline span<char> afio_namespace_string()
+ {
+ static char buffer[64];
+ static size_t length;
+ if(length)
+ return span<char>(buffer, length);
+ const char *src = AFIO_LOG_STRINGIFY(AFIO_V2_NAMESPACE);
+ char *bufferp = buffer;
+ for(; *src && (bufferp - buffer) < (ptrdiff_t) sizeof(buffer); src++)
+ {
+ if(*src != ' ')
+ *bufferp++ = *src;
+ }
+ *bufferp = 0;
+ length = bufferp - buffer;
+ return span<char>(buffer, length);
+ }
+ // Returns the Outcome namespace as a string
+ inline span<char> outcome_namespace_string()
+ {
+ static char buffer[64];
+ static size_t length;
+ if(length)
+ return span<char>(buffer, length);
+ const char *src = AFIO_LOG_STRINGIFY(OUTCOME_V2_NAMESPACE);
+ char *bufferp = buffer;
+ for(; *src && (bufferp - buffer) < (ptrdiff_t) sizeof(buffer); src++)
+ {
+ if(*src != ' ')
+ *bufferp++ = *src;
+ }
+ *bufferp = 0;
+ length = bufferp - buffer;
+ return span<char>(buffer, length);
+ }
+ // Strips a __PRETTY_FUNCTION__ of all instances of ::AFIO_V2_NAMESPACE:: and ::AFIO_V2_NAMESPACE::
+ inline void strip_pretty_function(char *out, size_t bytes, const char *in)
+ {
+ const span<char> remove1 = afio_namespace_string();
+ const span<char> remove2 = outcome_namespace_string();
+ for(--bytes; bytes && *in; --bytes)
+ {
+ if(!strncmp(in, remove1.data(), remove1.size()))
+ in += remove1.size();
+ if(!strncmp(in, remove2.data(), remove2.size()))
+ in += remove2.size();
+ *out++ = *in++;
+ }
+ *out = 0;
+ }
+ template <class T> void log_inst_to_info(T &&inst, const char *buffer) { AFIO_LOG_INFO(inst, buffer); }
+}
+AFIO_V2_NAMESPACE_END
+#ifdef _MSC_VER
+#define AFIO_LOG_FUNCTION_CALL(inst) \
+ if(log().log_level() >= log_level::info) \
+ { \
+ char buffer[256]; \
+ ::AFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __FUNCSIG__); \
+ ::AFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \
+ } \
+ AFIO_LOG_INST_TO_TLS(inst)
+#else
+#define AFIO_LOG_FUNCTION_CALL(inst) \
+ if(log().log_level() >= log_level::info) \
+ { \
+ char buffer[256]; \
+ ::AFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __PRETTY_FUNCTION__); \
+ ::AFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \
+ } \
+ AFIO_LOG_INST_TO_TLS(inst)
+#endif
+#else
+#define AFIO_LOG_INFO(inst, message)
+#define AFIO_LOG_FUNCTION_CALL(inst) AFIO_LOG_INST_TO_TLS(inst)
+#endif
+#if AFIO_LOGGING_LEVEL >= 5
+#define AFIO_LOG_DEBUG(inst, message) \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::debug, ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 5U)) ? nullptr : __func__, __LINE__)
+#else
+#define AFIO_LOG_DEBUG(inst, message)
+#endif
+#if AFIO_LOGGING_LEVEL >= 6
+#define AFIO_LOG_ALL(inst, message) \
+ ::AFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::all, (message), ::AFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (AFIO_LOG_BACKTRACE_LEVELS & (1U << 6U)) ? nullptr : __func__, __LINE__)
+#else
+#define AFIO_LOG_ALL(inst, message)
+#endif
+
+AFIO_V2_NAMESPACE_BEGIN
+
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+namespace detail
+{
+ template <class Src> inline void append_path_info(Src &src, std::string &ret)
+ {
+ if(QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id() == src._thread_id)
+ {
+ auto &tls = detail::tls_errored_results();
+ const char *path1 = tls.get(src._tls_path_id1), *path2 = tls.get(src._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(src._log_id != static_cast<uint32_t>(-1))
+ {
+ if(log().valid(src._log_id))
+ {
+ ret.append(" [location = ");
+ ret.append(location(log()[src._log_id]));
+ ret.append("]");
+ }
+ }
+#endif
+ }
+}
+#endif
+
+AFIO_V2_NAMESPACE_END
+
+
+#endif
diff --git a/include/afio/v2.0/status_code.hpp b/include/afio/v2.0/status_code.hpp
new file mode 100644
index 00000000..93185c77
--- /dev/null
+++ b/include/afio/v2.0/status_code.hpp
@@ -0,0 +1,464 @@
+/* Configures AFIO
+(C) 2018 Niall Douglas <http://www.nedproductions.biz/> (24 commits)
+File Created: June 2018
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License in the accompanying file
+Licence.txt or at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file Licence.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+#ifndef AFIO_STATUS_CODE_HPP
+#define AFIO_STATUS_CODE_HPP
+
+#include "logging.hpp"
+
+/* The SG14 status code implementation is quite profoundly different to the
+error code implementation. In the error code implementation, std::error_code
+is fixed by the standard library, so we wrap it with extra metadata into
+an error_info type. error_info then constructs off a code and a code domain
+tag.
+
+Status code, on the other hand, is templated and is designed for custom
+domains which can set arbitrary payloads. So we define custom domains and
+status codes for AFIO with these combinations:
+
+- win32_error{ DWORD }
+- ntkernel_error{ LONG }
+- posix_error{ int }
+- generic_error{ errc }
+
+Each of these is a separate AFIO custom status code domain. We also define
+an erased form of these custom domains, and that is typedefed to
+error_domain<intptr_t>::value_type.
+
+This design ensure that AFIO can be configured into either std-based error
+handling or SG14 experimental status code handling. It defaults to the latter
+as that (a) enables safe header only AFIO on Windows (b) produces better codegen
+(c) drags in far fewer STL headers.
+*/
+
+#if AFIO_EXPERIMENTAL_STATUS_CODE
+
+// Bring in a result implementation based on status_code
+#include "outcome/include/outcome/experimental/status_result.hpp"
+#include "outcome/include/outcome/try.hpp"
+
+AFIO_V2_NAMESPACE_BEGIN
+
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+
+namespace detail
+{
+ template <class T> struct error_domain_value_type
+ {
+ //! \brief The type of code
+ T sc{};
+
+ // The id of the thread where this failure occurred
+ uint32_t _thread_id{0};
+ // The TLS path store entry
+ uint16_t _tls_path_id1{static_cast<uint16_t>(-1)}, _tls_path_id2{static_cast<uint16_t>(-1)};
+ // The id of the relevant log entry in the AFIO log (if logging enabled)
+ size_t _log_id{static_cast<size_t>(-1)};
+
+ //! Default construction
+ error_domain_value_type() = default;
+
+ //! Implicitly constructs an instance
+ constexpr inline error_domain_value_type(T _sc)
+ : sc(_sc)
+ {
+ } // NOLINT
+
+ //! Compares to a T
+ constexpr bool operator==(const T &b) const noexcept { return sc == b; }
+ };
+}
+
+/*! \class error_domain
+\brief The SG14 status code domain for errors in AFIO.
+*/
+template <class BaseStatusCodeDomain> class error_domain : public BaseStatusCodeDomain
+{
+ friend class SYSTEM_ERROR2_NAMESPACE::status_code<error_domain>;
+ using _base = BaseStatusCodeDomain;
+
+public:
+ using string_ref = typename BaseStatusCodeDomain::string_ref;
+ using atomic_refcounted_string_ref = typename BaseStatusCodeDomain::atomic_refcounted_string_ref;
+
+ //! \brief The value type of errors in AFIO
+ using value_type = detail::error_domain_value_type<typename _base::value_type>;
+
+ error_domain() = default;
+ error_domain(const error_domain &) = default;
+ error_domain(error_domain &&) = default;
+ error_domain &operator=(const error_domain &) = default;
+ error_domain &operator=(error_domain &&) = default;
+ ~error_domain() = default;
+
+protected:
+ virtual inline string_ref _do_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final
+ {
+ assert(code.domain() == *this);
+ const auto &v = static_cast<const SYSTEM_ERROR2_NAMESPACE::status_code<error_domain> &>(code); // NOLINT
+ std::string ret = _base::_do_message(code).c_str();
+ detail::append_path_info(v.value(), ret);
+ char *p = (char *) malloc(ret.size() + 1);
+ if(p == nullptr)
+ {
+ return string_ref("Failed to allocate memory to store error string");
+ }
+ memcpy(p, ret.c_str(), ret.size() + 1);
+ return atomic_refcounted_string_ref(p, ret.size());
+ }
+};
+
+#else
+template <class BaseStatusCodeDomain> using error_domain = BaseStatusCodeDomain;
+#endif
+
+namespace detail
+{
+ using error_domain_value_system_code = error_domain_value_type<SYSTEM_ERROR2_NAMESPACE::system_code::value_type>;
+}
+
+//! An erased status code
+using error_code = SYSTEM_ERROR2_NAMESPACE::status_code<SYSTEM_ERROR2_NAMESPACE::erased<detail::error_domain_value_system_code>>;
+
+
+template <class T> using result = OUTCOME_V2_NAMESPACE::experimental::erased_result<T, error_code>;
+using OUTCOME_V2_NAMESPACE::success;
+using OUTCOME_V2_NAMESPACE::failure;
+using OUTCOME_V2_NAMESPACE::in_place_type;
+
+//! Choose an errc implementation
+using SYSTEM_ERROR2_NAMESPACE::errc;
+
+//! Helper for constructing an error code from an errc
+inline error_code generic_error(errc c);
+#ifndef _WIN32
+//! Helper for constructing an error code from a POSIX errno
+inline error_code posix_error(int c = errno);
+#else
+//! Helper for constructing an error code from a DWORD
+inline error_code win32_error(SYSTEM_ERROR2_NAMESPACE::win32::DWORD c = SYSTEM_ERROR2_NAMESPACE::win32::GetLastError());
+//! Helper for constructing an error code from a NTSTATUS
+inline error_code ntkernel_error(SYSTEM_ERROR2_NAMESPACE::win32::NTSTATUS c);
+#endif
+
+namespace detail
+{
+ inline std::ostream &operator<<(std::ostream &s, const error_code &v) { return s << "afio::error_code(" << v.message().c_str() << ")"; }
+}
+inline error_code error_from_exception(std::exception_ptr &&ep = std::current_exception(), error_code not_matched = generic_error(errc::resource_unavailable_try_again)) noexcept
+{
+ if(!ep)
+ {
+ return generic_error(errc::success);
+ }
+ try
+ {
+ std::rethrow_exception(ep);
+ }
+ catch(const std::invalid_argument & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::invalid_argument);
+ }
+ catch(const std::domain_error & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::argument_out_of_domain);
+ }
+ catch(const std::length_error & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::argument_list_too_long);
+ }
+ catch(const std::out_of_range & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::result_out_of_range);
+ }
+ catch(const std::logic_error & /*unused*/) /* base class for this group */
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::invalid_argument);
+ }
+ catch(const std::system_error &e) /* also catches ios::failure */
+ {
+ ep = std::exception_ptr();
+ if(e.code().category() == std::generic_category())
+ {
+ return generic_error(static_cast<errc>(static_cast<int>(e.code().value())));
+ }
+ // Don't know this error code category, so fall through
+ }
+ catch(const std::overflow_error & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::value_too_large);
+ }
+ catch(const std::range_error & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::result_out_of_range);
+ }
+ catch(const std::runtime_error & /*unused*/) /* base class for this group */
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::resource_unavailable_try_again);
+ }
+ catch(const std::bad_alloc & /*unused*/)
+ {
+ ep = std::exception_ptr();
+ return generic_error(errc::not_enough_memory);
+ }
+ catch(...)
+ {
+ }
+ return not_matched;
+}
+
+AFIO_V2_NAMESPACE_END
+
+
+#else // AFIO_EXPERIMENTAL_STATUS_CODE
+
+
+// Bring in a result implementation based on std::error_code
+#include "outcome/include/outcome.hpp"
+
+AFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ template <class Dest, class Src> inline void fill_failure_info(Dest &dest, const Src &src);
+}
+
+struct error_info;
+inline std::error_code make_error_code(error_info ei);
+
+/*! \struct error_info
+\brief The cause of the failure of an operation in AFIO.
+*/
+struct error_info
+{
+ friend inline std::error_code make_error_code(error_info ei);
+ template <class Src> friend inline void detail::append_path_info(Src &src, std::string &ret);
+ template <class Dest, class Src> friend inline void detail::fill_failure_info(Dest &dest, const Src &src);
+
+private:
+ // The error code for the failure
+ std::error_code ec;
+
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+ // The id of the thread where this failure occurred
+ uint32_t _thread_id{0};
+ // The TLS path store entry
+ uint16_t _tls_path_id1{static_cast<uint16_t>(-1)}, _tls_path_id2{static_cast<uint16_t>(-1)};
+ // The id of the relevant log entry in the AFIO log (if logging enabled)
+ size_t _log_id{static_cast<size_t>(-1)};
+
+public:
+#endif
+
+ //! Default constructor
+ error_info() = default;
+ // Explicit construction from an error code
+ explicit inline error_info(std::error_code _ec); // NOLINT
+ /* NOTE TO SELF: The error_info constructor implementation is in handle.hpp as we need that
+ defined before we can do useful logging.
+ */
+ //! Implicit 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) // NOLINT
+ : error_info(make_error_code(std::forward<ErrorCondEnum>(v)))
+ {
+ }
+
+ //! Retrieve the value of the error code
+ int value() const noexcept { return ec.value(); }
+ //! 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
+ {
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+ 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);
+ }
+ }
+#endif
+ return {};
+ }
+ //! 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
+ {
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+ 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);
+ }
+ }
+#endif
+ return {};
+ }
+ //! 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
+ {
+ std::string ret(ec.message());
+#ifndef AFIO_DISABLE_PATHS_IN_FAILURE_INFO
+ detail::append_path_info(*this, ret);
+#endif
+ return ret;
+ }
+ /*! 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_exception() const;
+};
+inline bool operator==(const error_info &a, const error_info &b)
+{
+ return make_error_code(a) == make_error_code(b);
+}
+inline bool operator!=(const error_info &a, const error_info &b)
+{
+ return make_error_code(a) != make_error_code(b);
+}
+OUTCOME_TEMPLATE(class ErrorCondEnum)
+OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
+inline bool operator==(const error_info &a, const ErrorCondEnum &b)
+{
+ return make_error_code(a) == std::error_condition(b);
+}
+OUTCOME_TEMPLATE(class ErrorCondEnum)
+OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
+inline bool operator==(const ErrorCondEnum &a, const error_info &b)
+{
+ return std::error_condition(a) == make_error_code(b);
+}
+OUTCOME_TEMPLATE(class ErrorCondEnum)
+OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
+inline bool operator!=(const error_info &a, const ErrorCondEnum &b)
+{
+ return make_error_code(a) != std::error_condition(b);
+}
+OUTCOME_TEMPLATE(class ErrorCondEnum)
+OUTCOME_TREQUIRES(OUTCOME_TPRED(std::is_error_condition_enum<ErrorCondEnum>::value))
+inline bool operator!=(const ErrorCondEnum &a, const error_info &b)
+{
+ return std::error_condition(a) != make_error_code(b);
+}
+#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(make_error_code(v))
+ {
+ 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_exception() on no-value observation
+inline void outcome_throw_as_system_error_with_payload(const error_info &ei)
+{
+ ei.throw_exception();
+}
+
+/*! \class error
+\brief The exception type synthesised and thrown when an `afio::result` or `afio::outcome` is no-value observed.
+*/
+class error : public filesystem::filesystem_error
+{
+public:
+ error_info ei;
+
+ //! Constructs from an error_info
+ explicit error(error_info _ei)
+ : filesystem::filesystem_error(_ei.message(), _ei.path1(), _ei.path2(), make_error_code(_ei))
+ , ei(_ei)
+ {
+ }
+};
+
+inline void error_info::throw_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;
+inline error_info error_from_exception(std::exception_ptr &&ep = std::current_exception(), std::error_code not_matched = std::make_error_code(std::errc::resource_unavailable_try_again)) noexcept
+{
+ return error_info(OUTCOME_V2_NAMESPACE::error_from_exception(std::move(ep), not_matched));
+}
+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");
+
+//! Choose an errc implementation
+using std::errc;
+
+//! Helper for constructing an error info from an errc
+inline error_info generic_error(errc c)
+{
+ return error_info(make_error_code(c));
+}
+#ifndef _WIN32
+//! Helper for constructing an error info from a POSIX errno
+inline error_info posix_error(int c = errno)
+{
+ return error_info(std::error_code(c, std::system_category()));
+}
+#endif
+
+AFIO_V2_NAMESPACE_END
+
+#endif // AFIO_EXPERIMENTAL_STATUS_CODE
+
+
+#endif