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>2020-09-05 00:40:31 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2020-09-05 00:40:31 +0300
commite5fc567e29066d836db8286d3d86c8e4eef49b54 (patch)
tree47e3d25c26547e749b74dac333f4ed9b345b9aca
parent2df37cb70bb3a1a47a55c4094d7dd26abf4ea0c1 (diff)
parente5842850d8ccb1ce3cae905e0af9ebad919da4cd (diff)
Merge branch 'develop'
-rw-r--r--cmake/QuickCppLibBootstrap.cmake10
-rw-r--r--cmake/tests.cmake1
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/detail/impl/config.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/reduce.ipp20
-rw-r--r--include/llfio/v2.0/detail/impl/windows/file_handle.ipp2
-rw-r--r--include/llfio/v2.0/detail/impl/windows/map_handle.ipp137
-rw-r--r--include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp2
-rw-r--r--include/llfio/v2.0/logging.hpp105
-rw-r--r--test/tests/issue0009.cpp93
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())