diff options
author | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-09-05 00:40:31 +0300 |
---|---|---|
committer | Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com> | 2020-09-05 00:40:31 +0300 |
commit | e5fc567e29066d836db8286d3d86c8e4eef49b54 (patch) | |
tree | 47e3d25c26547e749b74dac333f4ed9b345b9aca | |
parent | 2df37cb70bb3a1a47a55c4094d7dd26abf4ea0c1 (diff) | |
parent | e5842850d8ccb1ce3cae905e0af9ebad919da4cd (diff) |
Merge branch 'develop'
-rw-r--r-- | cmake/QuickCppLibBootstrap.cmake | 10 | ||||
-rw-r--r-- | cmake/tests.cmake | 1 | ||||
-rw-r--r-- | include/llfio/revision.hpp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/config.ipp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/file_handle.ipp | 6 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp | 2 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/reduce.ipp | 20 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/file_handle.ipp | 2 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/map_handle.ipp | 137 | ||||
-rw-r--r-- | include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp | 2 | ||||
-rw-r--r-- | include/llfio/v2.0/logging.hpp | 105 | ||||
-rw-r--r-- | test/tests/issue0009.cpp | 93 |
12 files changed, 303 insertions, 87 deletions
diff --git a/cmake/QuickCppLibBootstrap.cmake b/cmake/QuickCppLibBootstrap.cmake index 29a0a830..8efa0b26 100644 --- a/cmake/QuickCppLibBootstrap.cmake +++ b/cmake/QuickCppLibBootstrap.cmake @@ -28,12 +28,20 @@ foreach(item ${CMAKE_MODULE_PATH}) set(quickcpplib_done ON) endif() endforeach() +if(DEFINED quickcpplib_DIR) + find_package(quickcpplib QUIET CONFIG) + if(quickcpplib_FOUND) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${quickcpplib_DIR}/cmakelib") + set(CTEST_QUICKCPPLIB_SCRIPTS "${quickcpplib_DIR}/scripts") + set(quickcpplib_done ON) + endif() +endif() if(NOT quickcpplib_done) # CMAKE_SOURCE_DIR is the very topmost parent cmake project # CMAKE_CURRENT_SOURCE_DIR is the current cmake subproject if(NOT DEFINED CTEST_QUICKCPPLIB_CLONE_DIR) - # If there is a magic .quickcpplib_use_siblings directory above the topmost project, use sibling edition if(EXISTS "${CMAKE_SOURCE_DIR}/../.quickcpplib_use_siblings" AND NOT QUICKCPPLIB_DISABLE_SIBLINGS) + # If there is a magic .quickcpplib_use_siblings directory above the topmost project, use sibling edition set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/../quickcpplib/cmakelib") set(CTEST_QUICKCPPLIB_SCRIPTS "${CMAKE_SOURCE_DIR}/../quickcpplib/scripts") # Copy latest version of myself into end user diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 0bf01f1f..8448643e 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -12,6 +12,7 @@ set(llfio_TESTS "test/tests/file_handle_create_close/runner.cpp" "test/tests/file_handle_lock_unlock.cpp" "test/tests/handle_adapter_xor.cpp" + "test/tests/issue0009.cpp" "test/tests/issue0027.cpp" "test/tests/issue0028.cpp" "test/tests/large_pages.cpp" diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp index 91eeb3c5..6f0c2825 100644 --- a/include/llfio/revision.hpp +++ b/include/llfio/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 LLFIO_PREVIOUS_COMMIT_REF 21883faeb87fa31e209f9ed1de44b3aca89edce2 -#define LLFIO_PREVIOUS_COMMIT_DATE "2020-07-24 12:31:12 +00:00" -#define LLFIO_PREVIOUS_COMMIT_UNIQUE 21883fae +#define LLFIO_PREVIOUS_COMMIT_REF 4f4dbe1d95b7afc2b8e6d713611d391598546c8b +#define LLFIO_PREVIOUS_COMMIT_DATE "2020-09-04 17:40:59 +00:00" +#define LLFIO_PREVIOUS_COMMIT_UNIQUE 4f4dbe1d diff --git a/include/llfio/v2.0/detail/impl/config.ipp b/include/llfio/v2.0/detail/impl/config.ipp index c13dd469..b0b324e0 100644 --- a/include/llfio/v2.0/detail/impl/config.ipp +++ b/include/llfio/v2.0/detail/impl/config.ipp @@ -31,6 +31,12 @@ namespace detail #if defined(LLFIO_SOURCE) && !defined(LLFIO_DISABLE_SIZEOF_FILESYSTEM_PATH_CHECK) LLFIO_HEADERS_ONLY_FUNC_SPEC size_t sizeof_filesystem_path() noexcept { return sizeof(filesystem::path); } #endif + + LLFIO_HEADERS_ONLY_FUNC_SPEC char &thread_local_log_level() + { + static LLFIO_THREAD_LOCAL char v; + return v; + } } // namespace detail LLFIO_V2_NAMESPACE_END diff --git a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp index 4d431e92..96eb3a40 100644 --- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp @@ -771,7 +771,7 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte (void) outoffp; (void) len; (void) flags; - errno = EOPNOTSUPP; + errno = ENOSYS; return -1; #endif }; @@ -797,7 +797,7 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte off_t off_in = item.src.offset + thisoffset, off_out = item.src.offset + thisoffset + destoffsetdiff; if(_copy_file_range(_v.fd, &off_in, dest.native_handle().fd, &off_out, thisblock, 0) < 0) { - if((EXDEV != errno && EOPNOTSUPP != errno) || !emulate_if_unsupported) + if((EXDEV != errno && EOPNOTSUPP != errno && ENOSYS != errno) || !emulate_if_unsupported) { return posix_error(); } @@ -876,7 +876,7 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte { if(_zero_file_range(dest.native_handle().fd, item.src.offset + thisoffset + destoffsetdiff, thisblock) < 0) { - if(EOPNOTSUPP != errno || !emulate_if_unsupported) + if((EOPNOTSUPP != errno && ENOSYS != errno) || !emulate_if_unsupported) { return posix_error(); } diff --git a/include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp index 94e9bf9d..da5db0c0 100644 --- a/include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp @@ -102,7 +102,7 @@ result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type OUTCOME_TRYV(_sh.close()); return file_handle::truncate(newsize); } - if(!_sh.is_valid()) + if(!_sh.is_valid() || !_mh.is_valid()) { OUTCOME_TRY(auto &&ret, file_handle::truncate(newsize)); if(newsize > _reservation) diff --git a/include/llfio/v2.0/detail/impl/reduce.ipp b/include/llfio/v2.0/detail/impl/reduce.ipp index 7612e7ea..214ccdca 100644 --- a/include/llfio/v2.0/detail/impl/reduce.ipp +++ b/include/llfio/v2.0/detail/impl/reduce.ipp @@ -90,6 +90,11 @@ namespace algorithm { ntstat = ntwait(h, isb, deadline()); } + if(0xC0000056 /*STATUS_DELETE_PENDING*/ == ntstat) + { + // Can skip this fellow + return success(); + } } else { @@ -99,6 +104,11 @@ namespace algorithm { ntstat = ntwait(h, isb, deadline()); } + if(0xC0000056 /*STATUS_DELETE_PENDING*/ == ntstat) + { + // Can skip this fellow + return success(); + } if(ntstat >= 0) { // FILE_DELETE_ON_CLOSE open flag only works on non-directories. @@ -196,6 +206,11 @@ namespace algorithm { ntstat = ntwait(h, isb, deadline()); } + if(0xC0000056 /*STATUS_DELETE_PENDING*/ == ntstat) + { + // Can skip this fellow + return success(); + } } else { @@ -204,6 +219,11 @@ namespace algorithm { ntstat = ntwait(h, isb, deadline()); } + if(0xC0000056 /*STATUS_DELETE_PENDING*/ == ntstat) + { + // Can skip this fellow + return success(); + } } if(h != INVALID_HANDLE_VALUE) { diff --git a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp index b9137ce8..cfd4a387 100644 --- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp @@ -821,7 +821,7 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte } done = true; } - assert(done); + //assert(done); dest_length = destoffset + extent.length; truncate_back_on_failure = false; LLFIO_DEADLINE_TO_TIMEOUT_LOOP(d); diff --git a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp index 01a0897d..cca6dbcb 100644 --- a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp @@ -1,5 +1,5 @@ /* A handle to a source of mapped memory -(C) 2016-2017 Niall Douglas <http://www.nedproductions.biz/> (17 commits) +(C) 2016-2020 Niall Douglas <http://www.nedproductions.biz/> (17 commits) File Created: August 2016 @@ -142,7 +142,8 @@ result<section_handle> section_handle::section(file_handle &backing, extent_type return {buffer_, end}; }(); auto unique_id = backing.unique_id(); - QUICKCPPLIB_NAMESPACE::algorithm::string::to_hex_string(buffer.second, sizeof(unique_id) * 4, reinterpret_cast<char *>(unique_id.as_bytes), sizeof(unique_id)); + QUICKCPPLIB_NAMESPACE::algorithm::string::to_hex_string(buffer.second, sizeof(unique_id) * 4, reinterpret_cast<char *>(unique_id.as_bytes), + sizeof(unique_id)); buffer.second[32] = 0; _path.Buffer = buffer.first; _path.MaximumLength = (_path.Length = static_cast<USHORT>((32 + buffer.second - buffer.first) * sizeof(wchar_t))) + sizeof(wchar_t); @@ -315,7 +316,8 @@ template <class T> static inline T win32_round_up_to_allocation_size(T i) noexce i = (T)((LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<uintptr_t>(i) + 65535) & ~(65535)); // NOLINT return i; } -static inline result<void> win32_map_flags(native_handle_type &nativeh, DWORD &allocation, DWORD &prot, size_t &commitsize, bool enable_reservation, section_handle::flag _flag) +static inline result<void> win32_map_flags(native_handle_type &nativeh, DWORD &allocation, DWORD &prot, size_t &commitsize, bool enable_reservation, + section_handle::flag _flag) { prot = PAGE_NOACCESS; if(enable_reservation && ((_flag & section_handle::flag::nocommit) || (_flag == section_handle::flag::none))) @@ -372,24 +374,61 @@ static inline result<void> win32_map_flags(native_handle_type &nativeh, DWORD &a return success(); } -QUICKCPPLIB_BITFIELD_BEGIN(win32_map_sought){committed = 1U << 0U, freed = 1U << 1U, reserved = 1U << 2U} QUICKCPPLIB_BITFIELD_END(win32_map_sought) +QUICKCPPLIB_BITFIELD_BEGIN(win32_map_sought){ +committed = 1U << 0U, // Call F for committed pages +freed = 1U << 1U, // Call F for free pages +reserved = 1U << 2U, // Call F for reserved pages +only_bases = 1U << 3U // Only call F for AllocationBase changing regions only. You want this for maps of files, reservations etc. +} QUICKCPPLIB_BITFIELD_END(win32_map_sought) // Used to apply an operation to all maps within a region template <class F> static inline result<void> win32_maps_apply(byte *addr, size_t bytes, win32_map_sought sought, F &&f) { - /* Ok, so we have to be super careful here, because calling VirtualQuery() - somewhere in the middle of a region causes a linear scan until the beginning - or end of the region is found. For a reservation of say 2^42, that takes a - LONG time. + /* Ok, so we have to be super careful here, indeed this routine has been + completely rewritten three times now. Do NOT change it unless you are aware + of the following: - What we do instead is to query the current block, perform F on it, then query - it again to get the next point of change, query that, perhaps perform F on it, - and so on. + Version 1: + ---------- + We would call VirtualQuery() to find a matching region, perform + F on it, and then call VirtualQuery() on the address where the matching region + ended. Unfortunately, F might cause coalescing of the regions, and it turns out + that calling VirtualQuery() somewhere in the middle of a region + causes a linear scan until the beginning or end of the region is found. If the + region just unmapped was within say a reservation of 2^42, that takes a LONG time. + Users rightly complained about crap performance. - This causes twice the number of VirtualQuery() calls as ought to be necessary, - but such is this API's design. + Version 2: + ---------- + We would call VirtualQuery() to find a matching region, perform F on it, then query + the *same* block again to get the next point of change, query that, perhaps perform F on it, + and so on. This introduced twice the number of VirtualQuery() calls as ought to be necessary, + but this version worked well for nearly two years before the now-obvious race + condition was discovered: if the region was freed, and another thread performs + an allocation whose address just happens to land within the region just freed, the + second VirtualQuery() will now free another thread's allocation! + + Version 3: + ---------- + We iterate VirtualQuery() over the input byte range, storing all the matching results + into a vector. We then iterate the vector, calling the function. The dynamic memory + allocation is unfortunate, but we cache the vector storage thread locally. And, + generally speaking, a TLB shootdown is a lot more expensive than malloc. */ MEMORY_BASIC_INFORMATION mbi; + struct toinvoke_t + { + byte *addr; + size_t length; + void *base; + }; + static thread_local std::vector<toinvoke_t> toinvoke([] { + std::vector<toinvoke_t> ret; + ret.reserve(64); + return ret; + }()); + toinvoke.clear(); + //std::cout << "win32_maps_apply addr=" << addr << " size=" << bytes << std::endl; while(bytes > 0) { if(!VirtualQuery(addr, &mbi, sizeof(mbi))) @@ -397,21 +436,32 @@ static inline result<void> win32_maps_apply(byte *addr, size_t bytes, win32_map_ LLFIO_LOG_FATAL(nullptr, "map_handle::win32_maps_apply VirtualQuery() 1 failed"); abort(); } + //std::cout << "VQ1 baseaddr=" << mbi.BaseAddress << " size=" << mbi.RegionSize << " allocbase=" << mbi.AllocationBase << " state=" << mbi.State + // << " type=" << mbi.Type << std::endl; bool doit = false; doit = doit || ((MEM_COMMIT == mbi.State) && (sought & win32_map_sought::committed)); doit = doit || ((MEM_FREE == mbi.State) && (sought & win32_map_sought::freed)); doit = doit || ((MEM_RESERVE == mbi.State) && (sought & win32_map_sought::reserved)); if(doit) { - /* mbi.RegionSize will be that from mbi.AllocationBase to end of a reserved region, - so clamp to region size originally requested. - */ - OUTCOME_TRYV(f(reinterpret_cast<byte *>(mbi.BaseAddress), std::min(mbi.RegionSize, bytes))); - } - if(!VirtualQuery(addr, &mbi, sizeof(mbi))) - { - LLFIO_LOG_FATAL(nullptr, "map_handle::win32_maps_apply VirtualQuery() 2 failed"); - abort(); + if(!(sought & win32_map_sought::only_bases)) + { + /* mbi.RegionSize will be that from mbi.BaseAddress to end of a reserved region, + so clamp to region size originally requested. + */ + toinvoke.push_back({reinterpret_cast<byte *>(mbi.BaseAddress), std::min(mbi.RegionSize, bytes), mbi.AllocationBase}); + } + else + { + if(toinvoke.empty() || mbi.AllocationBase != toinvoke.back().base) + { + toinvoke.push_back({reinterpret_cast<byte *>(mbi.BaseAddress), std::min(mbi.RegionSize, bytes), mbi.AllocationBase}); + } + else if(!toinvoke.empty()) + { + toinvoke.back().length += std::min(mbi.RegionSize, bytes); + } + } } addr += mbi.RegionSize; if(mbi.RegionSize < bytes) @@ -423,6 +473,11 @@ static inline result<void> win32_maps_apply(byte *addr, size_t bytes, win32_map_ bytes = 0; } } + for(auto &i : toinvoke) + { + //std::cout << "f(" << i.addr << ", " << i.length << ")" << std::endl; + OUTCOME_TRY(f(i.addr, i.length)); + } return success(); } // Only for memory allocated with VirtualAlloc. We can special case decommitting or releasing @@ -474,7 +529,8 @@ result<void> map_handle::close() noexcept { OUTCOME_TRYV(barrier(barrier_kind::wait_all)); } - OUTCOME_TRYV(win32_maps_apply(_addr, _reservation, win32_map_sought::committed, [](byte *addr, size_t /* unused */) -> result<void> { + OUTCOME_TRYV( + win32_maps_apply(_addr, _reservation, win32_map_sought::committed | win32_map_sought::only_bases, [](byte *addr, size_t /* unused */) -> result<void> { NTSTATUS ntstat = NtUnmapViewOfSection(GetCurrentProcess(), addr); if(ntstat < 0) { @@ -505,7 +561,8 @@ native_handle_type map_handle::release() noexcept return {}; } -map_handle::io_result<map_handle::const_buffers_type> map_handle::_do_barrier(map_handle::io_request<map_handle::const_buffers_type> reqs, barrier_kind kind, deadline d) noexcept +map_handle::io_result<map_handle::const_buffers_type> map_handle::_do_barrier(map_handle::io_request<map_handle::const_buffers_type> reqs, barrier_kind kind, + deadline d) noexcept { LLFIO_LOG_FUNCTION_CALL(this); byte *addr = _addr + reqs.offset; @@ -682,14 +739,15 @@ result<map_handle::size_type> map_handle::truncate(size_type newsize, bool /* un if(newsize < _reservation) { // If newsize isn't exactly a previous extension, this will fail, same as for the VirtualAlloc case - OUTCOME_TRYV(win32_maps_apply(_addr + newsize, _reservation - newsize, win32_map_sought::committed, [](byte *addr, size_t /* unused */) -> result<void> { - NTSTATUS ntstat = NtUnmapViewOfSection(GetCurrentProcess(), addr); - if(ntstat < 0) - { - return ntkernel_error(ntstat); - } - return success(); - })); + OUTCOME_TRYV(win32_maps_apply(_addr + newsize, _reservation - newsize, win32_map_sought::committed | win32_map_sought::only_bases, + [](byte *addr, size_t /* unused */) -> result<void> { + NTSTATUS ntstat = NtUnmapViewOfSection(GetCurrentProcess(), addr); + if(ntstat < 0) + { + return ntkernel_error(ntstat); + } + return success(); + })); _reservation = newsize; _length = (length - _offset < newsize) ? (length - _offset) : newsize; // length of backing, not reservation return _reservation; @@ -750,13 +808,14 @@ result<map_handle::buffer_type> map_handle::commit(buffer_type region, section_h prot = PAGE_EXECUTE; } region = utils::round_to_page_size_larger(region, _pagesize); - OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), win32_map_sought::committed | win32_map_sought::freed | win32_map_sought::reserved, [prot](byte *addr, size_t bytes) -> result<void> { - if(VirtualAlloc(addr, bytes, MEM_COMMIT, prot) == nullptr) - { - return win32_error(); - } - return success(); - })); + OUTCOME_TRYV(win32_maps_apply(region.data(), region.size(), win32_map_sought::committed | win32_map_sought::freed | win32_map_sought::reserved, + [prot](byte *addr, size_t bytes) -> result<void> { + if(VirtualAlloc(addr, bytes, MEM_COMMIT, prot) == nullptr) + { + return win32_error(); + } + return success(); + })); return region; } diff --git a/include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp index 2409fd49..beef8cb8 100644 --- a/include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp @@ -112,7 +112,7 @@ result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type OUTCOME_TRYV(_sh.close()); return file_handle::truncate(newsize); } - if(!_sh.is_valid()) + if(!_sh.is_valid() || !_mh.is_valid()) { OUTCOME_TRY(auto &&ret, file_handle::truncate(newsize)); if(newsize > _reservation) diff --git a/include/llfio/v2.0/logging.hpp b/include/llfio/v2.0/logging.hpp index 1971bf32..1dff759a 100644 --- a/include/llfio/v2.0/logging.hpp +++ b/include/llfio/v2.0/logging.hpp @@ -33,10 +33,25 @@ Distributed under the Boost Software License, Version 1.0. */ LLFIO_V2_NAMESPACE_BEGIN +//! Enum for the log level +using log_level = QUICKCPPLIB_NAMESPACE::ringbuffer_log::level; + +namespace detail +{ + LLFIO_HEADERS_ONLY_FUNC_SPEC char &thread_local_log_level(); + struct thread_local_log_level_filter + { + log_level operator()(log_level v) const { return std::min(v, (log_level) thread_local_log_level()); } + }; +} // namespace detail + +//! Type of the logger +using log_implementation_type = QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<LLFIO_LOGGING_MEMORY, detail::thread_local_log_level_filter>; + //! The log used by LLFIO -inline LLFIO_DECL QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<LLFIO_LOGGING_MEMORY> &log() noexcept +inline LLFIO_DECL log_implementation_type &log() noexcept { - static QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<LLFIO_LOGGING_MEMORY> _log(static_cast<QUICKCPPLIB_NAMESPACE::ringbuffer_log::level>(LLFIO_LOGGING_LEVEL)); + static log_implementation_type _log(static_cast<QUICKCPPLIB_NAMESPACE::ringbuffer_log::level>(LLFIO_LOGGING_LEVEL)); #ifdef LLFIO_LOG_TO_OSTREAM if(_log.immediate() != &LLFIO_LOG_TO_OSTREAM) { @@ -45,8 +60,7 @@ inline LLFIO_DECL QUICKCPPLIB_NAMESPACE::ringbuffer_log::simple_ringbuffer_log<L #endif return _log; } -//! Enum for the log level -using log_level = QUICKCPPLIB_NAMESPACE::ringbuffer_log::level; + //! RAII class for temporarily adjusting the log level for the current thread class log_level_guard { @@ -59,11 +73,11 @@ public: 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()) + : _v((log_level) detail::thread_local_log_level()) { - log().thread_log_level(n); + reinterpret_cast<log_level &>(detail::thread_local_log_level()) = n; } - ~log_level_guard() { log().thread_log_level(_v); } + ~log_level_guard() {reinterpret_cast<log_level &>(detail::thread_local_log_level()) = _v; } }; // Infrastructure for recording the current path for when failure occurs @@ -146,7 +160,10 @@ namespace detail ~tls_current_handle_holder() = default; template <class T> explicit tls_current_handle_holder(T && /*unused*/) {} }; -#define LLFIO_LOG_INST_TO_TLS(inst) ::LLFIO_V2_NAMESPACE::detail::tls_current_handle_holder<std::is_base_of<::LLFIO_V2_NAMESPACE::handle, std::decay_t<std::remove_pointer_t<decltype(inst)>>>::value> LLFIO_UNIQUE_NAME(inst) +#define LLFIO_LOG_INST_TO_TLS(inst) \ + ::LLFIO_V2_NAMESPACE::detail::tls_current_handle_holder< \ + std::is_base_of<::LLFIO_V2_NAMESPACE::handle, std::decay_t<std::remove_pointer_t<decltype(inst)>>>::value> \ + LLFIO_UNIQUE_NAME(inst) } // namespace detail #else // LLFIO_DISABLE_PATHS_IN_FAILURE_INFO #define LLFIO_LOG_INST_TO_TLS(inst) @@ -156,36 +173,44 @@ LLFIO_V2_NAMESPACE_END #ifndef LLFIO_LOG_FATAL_TO_CERR #include <cstdio> -#define LLFIO_LOG_FATAL_TO_CERR(expr) \ - fprintf(stderr, "%s\n", (expr)); \ +#define LLFIO_LOG_FATAL_TO_CERR(expr) \ + fprintf(stderr, "%s\n", (expr)); \ fflush(stderr) #endif #endif // LLFIO_LOGGING_LEVEL #if LLFIO_LOGGING_LEVEL >= 1 -#define LLFIO_LOG_FATAL(inst, message) \ - { \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::fatal, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 1U)) ? nullptr : __func__, __LINE__); \ - LLFIO_LOG_FATAL_TO_CERR(message); \ +#define LLFIO_LOG_FATAL(inst, message) \ + { \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::fatal, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 1U)) ? nullptr : __func__, __LINE__); \ + LLFIO_LOG_FATAL_TO_CERR(message); \ } #else #define LLFIO_LOG_FATAL(inst, message) LLFIO_LOG_FATAL_TO_CERR(message) #endif #if LLFIO_LOGGING_LEVEL >= 2 -#define LLFIO_LOG_ERROR(inst, message) \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::error, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 2U)) ? nullptr : __func__, __LINE__) +#define LLFIO_LOG_ERROR(inst, message) \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::error, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 2U)) ? nullptr : __func__, __LINE__) #else #define LLFIO_LOG_ERROR(inst, message) #endif #if LLFIO_LOGGING_LEVEL >= 3 -#define LLFIO_LOG_WARN(inst, message) \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::warn, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 3U)) ? nullptr : __func__, __LINE__) +#define LLFIO_LOG_WARN(inst, message) \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::warn, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 3U)) ? nullptr : __func__, __LINE__) #else #define LLFIO_LOG_WARN(inst, message) #endif #if LLFIO_LOGGING_LEVEL >= 4 -#define LLFIO_LOG_INFO(inst, message) \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::info, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 4U)) ? nullptr : __func__, __LINE__) +#define LLFIO_LOG_INFO(inst, message) \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::info, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 4U)) ? nullptr : __func__, __LINE__) // Need to expand out our namespace into a string #define LLFIO_LOG_STRINGIFY9(s) #s "::" @@ -255,22 +280,22 @@ namespace detail } // namespace detail LLFIO_V2_NAMESPACE_END #ifdef _MSC_VER -#define LLFIO_LOG_FUNCTION_CALL(inst) \ - if(log().log_level() >= log_level::info) \ - { \ - char buffer[256]; \ - ::LLFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __FUNCSIG__); \ - ::LLFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \ - } \ +#define LLFIO_LOG_FUNCTION_CALL(inst) \ + if(log().log_level() >= log_level::info) \ + { \ + char buffer[256]; \ + ::LLFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __FUNCSIG__); \ + ::LLFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \ + } \ LLFIO_LOG_INST_TO_TLS(inst) #else -#define LLFIO_LOG_FUNCTION_CALL(inst) \ - if(log().log_level() >= log_level::info) \ - { \ - char buffer[256]; \ - ::LLFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __PRETTY_FUNCTION__); \ - ::LLFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \ - } \ +#define LLFIO_LOG_FUNCTION_CALL(inst) \ + if(log().log_level() >= log_level::info) \ + { \ + char buffer[256]; \ + ::LLFIO_V2_NAMESPACE::detail::strip_pretty_function(buffer, sizeof(buffer), __PRETTY_FUNCTION__); \ + ::LLFIO_V2_NAMESPACE::detail::log_inst_to_info(inst, buffer); \ + } \ LLFIO_LOG_INST_TO_TLS(inst) #endif #else @@ -278,14 +303,18 @@ LLFIO_V2_NAMESPACE_END #define LLFIO_LOG_FUNCTION_CALL(inst) LLFIO_LOG_INST_TO_TLS(inst) #endif #if LLFIO_LOGGING_LEVEL >= 5 -#define LLFIO_LOG_DEBUG(inst, message) \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::debug, ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 5U)) ? nullptr : __func__, __LINE__) +#define LLFIO_LOG_DEBUG(inst, message) \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::debug, ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 5U)) ? nullptr : __func__, __LINE__) #else #define LLFIO_LOG_DEBUG(inst, message) #endif #if LLFIO_LOGGING_LEVEL >= 6 -#define LLFIO_LOG_ALL(inst, message) \ - ::LLFIO_V2_NAMESPACE::log().emplace_back(QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::all, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 6U)) ? nullptr : __func__, __LINE__) +#define LLFIO_LOG_ALL(inst, message) \ + ::LLFIO_V2_NAMESPACE::log().emplace_back( \ + QUICKCPPLIB_NAMESPACE::ringbuffer_log::level::all, (message), ::LLFIO_V2_NAMESPACE::detail::unsigned_integer_cast<unsigned>(inst), \ + QUICKCPPLIB_NAMESPACE::utils::thread::this_thread_id(), (LLFIO_LOG_BACKTRACE_LEVELS & (1U << 6U)) ? nullptr : __func__, __LINE__) #else #define LLFIO_LOG_ALL(inst, message) #endif diff --git a/test/tests/issue0009.cpp b/test/tests/issue0009.cpp new file mode 100644 index 00000000..ca15cf43 --- /dev/null +++ b/test/tests/issue0009.cpp @@ -0,0 +1,93 @@ +/* Integration test kernel for issue #9 map_handle and mapped_file_handle are very slow with large address reservations on Windows +(C) 2020 Niall Douglas <http://www.nedproductions.biz/> (2 commits) +File Created: Sept 2020 + + +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) +*/ + +#include "../test_kernel_decl.hpp" + +static inline void TestIssue09a() +{ + namespace llfio = LLFIO_V2_NAMESPACE; + auto fh = llfio::file_handle::temp_file().value(); + fh.truncate(1).value(); + auto sh = llfio::section_handle::section(fh).value(); + sh.truncate(1ULL << 40ULL).value(); + auto mh = llfio::map_handle::map(sh, 1ULL << 35ULL).value(); + auto *addr1 = mh.address(); + mh.truncate(1ULL << 36ULL).value(); + { + auto *addr2 = mh.address(); + BOOST_CHECK(addr1 == addr2); + } + mh.truncate(1ULL << 37ULL).value(); + { + auto *addr2 = mh.address(); + BOOST_CHECK(addr1 == addr2); + } + mh.truncate(1ULL << 38ULL).value(); + { + auto *addr2 = mh.address(); + BOOST_CHECK(addr1 == addr2); + } + mh.truncate(1ULL << 39ULL).value(); + { + auto *addr2 = mh.address(); + BOOST_CHECK(addr1 == addr2); + } + mh.truncate(1ULL << 40ULL).value(); + { + auto *addr2 = mh.address(); + BOOST_CHECK(addr1 == addr2); + } + auto begin = std::chrono::steady_clock::now(); + mh.close().value(); + sh.close().value(); + fh.close().value(); + auto end = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); + std::cout << "Closing a map_handle (file) with six, appended, very large reservations up to 2^40 took " << diff << "ms." << std::endl; + BOOST_CHECK(diff < 250); +} + +static inline void TestIssue09b() +{ + namespace llfio = LLFIO_V2_NAMESPACE; + auto mh = llfio::map_handle::reserve(1ULL << 43ULL).value(); + const auto sixth = mh.length() / 6; + mh.commit({mh.address() + sixth * 0, 1}).value(); + mh.commit({mh.address() + sixth * 1, 1}).value(); + mh.commit({mh.address() + sixth * 2, 1}).value(); + mh.commit({mh.address() + sixth * 3, 1}).value(); + mh.commit({mh.address() + sixth * 4, 1}).value(); + mh.commit({mh.address() + sixth * 5, 1}).value(); + auto begin = std::chrono::steady_clock::now(); + mh.do_not_store({mh.address(), mh.length()}).value(); + auto end = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); + std::cout << "Discard a map_handle (reservation) with six small commits within a reservation of 2^43 took " << diff << "ms." << std::endl; + BOOST_CHECK(diff < 250); +} + +KERNELTEST_TEST_KERNEL(regression, llfio, issues, 9a, + "Tests issue #9 map_handle and mapped_file_handle are very slow with large address reservations on Windows", TestIssue09a()) +KERNELTEST_TEST_KERNEL(regression, llfio, issues, 9b, + "Tests issue #9 map_handle and mapped_file_handle are very slow with large address reservations on Windows", TestIssue09b()) |