diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-06-19 12:00:20 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2018-06-19 12:00:20 +0300 |
commit | 5854802f283de2b9c5026ea1a905ffdde1b09034 (patch) | |
tree | f4501b74338e20481acf4fa2f2784bf1fe996b53 | |
parent | 128ff433151efbad5585de55c3e365a982ba1acb (diff) |
Add signal guard to map_handle::write()
-rw-r--r-- | include/afio/revision.hpp | 6 | ||||
-rw-r--r-- | include/afio/v2.0/algorithm/shared_fs_mutex/memory_map.hpp | 2 | ||||
-rw-r--r-- | include/afio/v2.0/config.hpp | 6 | ||||
-rw-r--r-- | include/afio/v2.0/detail/impl/posix/map_handle.ipp | 80 | ||||
-rw-r--r-- | include/afio/v2.0/detail/impl/windows/map_handle.ipp | 57 | ||||
-rw-r--r-- | include/afio/v2.0/map_handle.hpp | 17 | ||||
m--------- | include/afio/v2.0/outcome | 0 | ||||
m--------- | include/afio/v2.0/quickcpplib | 0 |
8 files changed, 123 insertions, 45 deletions
diff --git a/include/afio/revision.hpp b/include/afio/revision.hpp index 976a7951..f8266792 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 f976a6f1277af7a84744f789d1a3f69aed345b68 -#define AFIO_PREVIOUS_COMMIT_DATE "2018-06-08 12:59:43 +00:00" -#define AFIO_PREVIOUS_COMMIT_UNIQUE f976a6f1 +#define AFIO_PREVIOUS_COMMIT_REF 128ff433151efbad5585de55c3e365a982ba1acb +#define AFIO_PREVIOUS_COMMIT_DATE "2018-06-18 20:18:55 +00:00" +#define AFIO_PREVIOUS_COMMIT_UNIQUE 128ff433 diff --git a/include/afio/v2.0/algorithm/shared_fs_mutex/memory_map.hpp b/include/afio/v2.0/algorithm/shared_fs_mutex/memory_map.hpp index 321f1bff..b71de0b2 100644 --- a/include/afio/v2.0/algorithm/shared_fs_mutex/memory_map.hpp +++ b/include/afio/v2.0/algorithm/shared_fs_mutex/memory_map.hpp @@ -29,7 +29,7 @@ Distributed under the Boost Software License, Version 1.0. #include "base.hpp" #ifdef __has_include -#if __has_include("../../../quickcpplib/include/algorithm/hash.hpp") +#if __has_include("../../quickcpplib/include/algorithm/hash.hpp") #include "../../quickcpplib/include/algorithm/hash.hpp" #include "../../quickcpplib/include/algorithm/small_prng.hpp" #include "../../quickcpplib/include/spinlock.hpp" diff --git a/include/afio/v2.0/config.hpp b/include/afio/v2.0/config.hpp index d8046bc3..2e8016fc 100644 --- a/include/afio/v2.0/config.hpp +++ b/include/afio/v2.0/config.hpp @@ -441,7 +441,7 @@ public: ~error_domain() = default; protected: - virtual inline string_ref _message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final; + 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; @@ -1045,11 +1045,11 @@ namespace detail #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>::_message(const SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept // NOLINT +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::_message(code).c_str(); + 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) diff --git a/include/afio/v2.0/detail/impl/posix/map_handle.ipp b/include/afio/v2.0/detail/impl/posix/map_handle.ipp index 4742b6b1..44860dcf 100644 --- a/include/afio/v2.0/detail/impl/posix/map_handle.ipp +++ b/include/afio/v2.0/detail/impl/posix/map_handle.ipp @@ -25,6 +25,8 @@ Distributed under the Boost Software License, Version 1.0. #include "../../../map_handle.hpp" #include "../../../utils.hpp" +#include "../../../quickcpplib/include/signal_guard.hpp" + #include <sys/mman.h> AFIO_V2_NAMESPACE_BEGIN @@ -544,23 +546,67 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::write(io_reque AFIO_LOG_FUNCTION_CALL(this); byte *addr = _addr + reqs.offset; size_type togo = reqs.offset < _length ? static_cast<size_type>(_length - reqs.offset) : 0; - for(const_buffer_type &req : reqs.buffers) - { - if(togo != 0u) - { - if(req.len > togo) - { - req.len = togo; - } - memcpy(addr, req.data, req.len); - req.data = addr; - addr += req.len; - togo -= req.len; - } - else - { - req.len = 0; - } + if(QUICKCPPLIB_NAMESPACE::signal_guard::signal_guard(QUICKCPPLIB_NAMESPACE::signal_guard::signalc::undefined_memory_access, + [&] { + for(const_buffer_type &req : reqs.buffers) + { + if(togo != 0u) + { + if(req.len > togo) + { + req.len = togo; + } + memcpy(addr, req.data, req.len); + req.data = addr; + addr += req.len; + togo -= req.len; + } + else + { + req.len = 0; + } + } + return false; + }, + [&](QUICKCPPLIB_NAMESPACE::signal_guard::signalc /* unused */, const void *_info, const void *_context) { + assert(signo == QUICKCPPLIB_NAMESPACE::signal_guard::signalc::undefined_memory_access); + const auto *info = (const siginfo_t *) _info; + assert(info->si_signo == SIGBUS); + auto *causingaddr = (byte *) info->si_addr; + if(causingaddr < _addr || causingaddr >= (_addr + _length)) + { + // Not caused by this map + struct sigaction sa; + sigaction(SIGBUS, nullptr, &sa); + if(sa.sa_flags & SA_SIGINFO) + { + sa.sa_sigaction(SIGBUS, (siginfo_t *) info, (void *) _context); + } + else if(sa.sa_handler != SIG_IGN) + { + if(sa.sa_handler != SIG_DFL) + { + sa.sa_handler(SIGBUS); + } + // Painful ... + struct sigaction myformer, def; + memset(&def, 0, sizeof(def)); + def.sa_handler = SIG_DFL; + sigaction(SIGBUS, &def, &myformer); + sigset_t myformer2; + sigset_t def2; + sigemptyset(&def2); + sigaddset(&def2, SIGBUS); + pthread_sigmask(SIG_UNBLOCK, &def2, &myformer2); + pthread_kill(pthread_self(), SIGBUS); + sigaction(SIGBUS, &myformer, nullptr); + } + abort(); + } + return true; + })) + { + return errc::no_space_on_device; } return reqs.buffers; } diff --git a/include/afio/v2.0/detail/impl/windows/map_handle.ipp b/include/afio/v2.0/detail/impl/windows/map_handle.ipp index 50b0e6d0..1331fb01 100644 --- a/include/afio/v2.0/detail/impl/windows/map_handle.ipp +++ b/include/afio/v2.0/detail/impl/windows/map_handle.ipp @@ -27,6 +27,7 @@ Distributed under the Boost Software License, Version 1.0. #include "import.hpp" #include "../../../quickcpplib/include/algorithm/hash.hpp" +#include "../../../quickcpplib/include/signal_guard.hpp" AFIO_V2_NAMESPACE_BEGIN @@ -812,23 +813,45 @@ map_handle::io_result<map_handle::const_buffers_type> map_handle::write(io_reque AFIO_LOG_FUNCTION_CALL(this); byte *addr = _addr + reqs.offset; size_type togo = reqs.offset < _length ? static_cast<size_type>(_length - reqs.offset) : 0; - for(const_buffer_type &req : reqs.buffers) - { - if(togo != 0u) - { - if(req.len > togo) - { - req.len = togo; - } - memcpy(addr, req.data, req.len); - req.data = addr; - addr += req.len; - togo -= req.len; - } - else - { - req.len = 0; - } + if(QUICKCPPLIB_NAMESPACE::signal_guard::signal_guard(QUICKCPPLIB_NAMESPACE::signal_guard::signalc::undefined_memory_access, + [&] { + for(const_buffer_type &req : reqs.buffers) + { + if(togo != 0u) + { + if(req.len > togo) + { + req.len = togo; + } + memcpy(addr, req.data, req.len); + req.data = addr; + addr += req.len; + togo -= req.len; + } + else + { + req.len = 0; + } + } + return false; + }, + [&](QUICKCPPLIB_NAMESPACE::signal_guard::signalc signo, const void *_info, const void * /*unused*/) { + assert(signo == QUICKCPPLIB_NAMESPACE::signal_guard::signalc::undefined_memory_access); + const auto *info = (const _EXCEPTION_RECORD *) _info; + assert(info->ExceptionCode == EXCEPTION_IN_PAGE_ERROR); + // info->ExceptionInformation[0] = 0=read 1=write 8=DEP + // info->ExceptionInformation[1] = causing address + // info->ExceptionInformation[2] = NTSTATUS causing exception + auto *causingaddr = (byte *) info->ExceptionInformation[1]; + if(causingaddr < _addr || causingaddr >= (_addr + _length)) + { + // Not caused by this map + RaiseException(info->ExceptionCode, info->ExceptionFlags, info->NumberParameters, info->ExceptionInformation); + } + return true; + })) + { + return errc::no_space_on_device; } return reqs.buffers; } diff --git a/include/afio/v2.0/map_handle.hpp b/include/afio/v2.0/map_handle.hpp index 567c7c5c..8c66450a 100644 --- a/include/afio/v2.0/map_handle.hpp +++ b/include/afio/v2.0/map_handle.hpp @@ -504,9 +504,11 @@ public: /*! \brief Read data from the mapped view. - \note Because this implementation never copies memory, you can pass in buffers with a null address. + \note Because this implementation never copies memory, you can pass in buffers with a null address. As this + function never reads any memory, no attempt to trap signal raises can be made, this falls onto the user of + this function. See `QUICKCPPLIB_NAMESPACE::signal_guard` for a helper function. - \return The buffers read, which will never be the buffers input because they will point into the mapped view. + \return The buffers read, which will never be the buffers input, because they will point into the mapped view. The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred. \param reqs A scatter-gather and offset request. \param d Ignored. @@ -519,12 +521,19 @@ public: /*! \brief Write data to the mapped view. + \note This call traps signals and structured exception throws using `QUICKCPPLIB_NAMESPACE::signal_guard`. + Instantiating a `QUICKCPPLIB_NAMESPACE::signal_guard_install` somewhere much higher up in the call stack + will improve performance enormously. The signal guard may cost less than 100 CPU cycles depending on how + you configure it. If you don't want the guard, you can write memory directly using `address()`. + \return The buffers written, which will never be the buffers input because they will point at where the data was copied into the mapped view. The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred. \param reqs A scatter-gather and offset request. \param d Ignored. - \errors None, though the various signals and structured exception throws common to using memory maps may occur. - \mallocs None. + \errors If during the attempt to write the buffers to the map a `SIGBUS` or `EXCEPTION_IN_PAGE_ERROR` is raised, + an error code comparing equal to `errc::no_space_on_device` will be returned. This may not always be the cause + of the raised signal, but it is by far the most likely. + \mallocs None if a `QUICKCPPLIB_NAMESPACE::signal_guard_install` is already instanced. */ AFIO_MAKE_FREE_FUNCTION AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override; diff --git a/include/afio/v2.0/outcome b/include/afio/v2.0/outcome -Subproject 95e0a2210dc9fe22f78117be79550197840c4ab +Subproject 4eb1466c77a0f8c3573f7cb1cb613b977c9758c diff --git a/include/afio/v2.0/quickcpplib b/include/afio/v2.0/quickcpplib -Subproject 4f428d18e58c4edd3f8eaf4da36e3d6b717b11a +Subproject 20df7f8420f1d8f71fe504b84609a9d9863495c |