diff --git a/CMakeLists.txt b/CMakeLists.txt index e04a36382..c1420f33c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ if(MSVC) endif() endif() if(CLANG OR GCC) - CheckCXXHasCoroutines(_CLANG_GCC "-std=c++14 -fcoroutines-ts") + CheckCXXHasCoroutines(_CLANG_GCC "-fcoroutines-ts") if(CXX_HAS_COROUTINES_CLANG_GCC) all_compile_options(PUBLIC "-fcoroutines-ts") all_compile_definitions(PUBLIC "LLFIO_HAVE_COROUTINES=1") @@ -176,6 +176,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR APPLE) all_link_libraries(PUBLIC ${libcxx_cxxexperimental}) endif() # Set any macros this library requires +all_compile_definitions(PRIVATE LLFIO_INCLUDE_ASYNC_FILE_HANDLE=1 LLFIO_INCLUDE_STORAGE_PROFILE=1) +foreach(target ${llfio_EXAMPLE_TARGETS}) + target_compile_definitions(${target} PRIVATE LLFIO_INCLUDE_ASYNC_FILE_HANDLE=1 LLFIO_INCLUDE_STORAGE_PROFILE=1) +endforeach() if(LLFIO_USE_EXPERIMENTAL_SG14_STATUS_CODE) all_compile_definitions(PUBLIC LLFIO_EXPERIMENTAL_STATUS_CODE=1) endif() @@ -208,7 +212,10 @@ endif() # For all possible configurations of this library, add each test include(QuickCppLibMakeStandardTests) -# For each test target, link to kerneltest +# For each test target, set definitions and linkage +foreach(target ${llfio_COMPILE_TEST_TARGETS} ${llfio_TEST_TARGETS}) + target_compile_definitions(${target} PRIVATE LLFIO_INCLUDE_ASYNC_FILE_HANDLE=1 LLFIO_INCLUDE_STORAGE_PROFILE=1) +endforeach() foreach(test_target ${llfio_TEST_TARGETS}) target_link_libraries(${test_target} PRIVATE kerneltest::hl) endforeach() diff --git a/doc/html b/doc/html index b5c88a3fa..11e1d127e 160000 --- a/doc/html +++ b/doc/html @@ -1 +1 @@ -Subproject commit b5c88a3fa6f2f11f5f10ad668b7b6b04dd819a45 +Subproject commit 11e1d127ed5ff762f836238235cd3c57fc434b67 diff --git a/example/ts_examples.cpp b/example/ts_examples.cpp index b93caf18b..b0f81cf54 100644 --- a/example/ts_examples.cpp +++ b/example/ts_examples.cpp @@ -72,6 +72,7 @@ inline io_handle::buffers_type read_all(io_handle &h, io_handle::io_request barrier(io_request reqs = io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept override; /*! Clone this handle to a different io_service (copy constructor is disabled to avoid accidental copying) \errors Any of the values POSIX dup() or DuplicateHandle() can return. @@ -367,10 +367,7 @@ class LLFIO_DECL async_file_handle : public file_handle which always write barrier the entire file. Supplying a default initialised reqs write barriers the entire file. \param completion A callable to call upon i/o completion. Spec is `void(async_file_handle *, io_result &)`. Note that buffers returned may not be buffers input, see documentation for `barrier()`. - \param wait_for_device True if you want the call to wait until data reaches storage and that storage - has acknowledged the data is physically written. Slow. - \param and_metadata True if you want the call to sync the metadata for retrieving the writes before the - barrier after a sudden power loss event. Slow. + \param kind Which kind of write reordering barrier to perform. \param mem Optional span of memory to use to avoid using `calloc()`. Note span MUST be all bits zero on entry. \errors As for `barrier()`, plus `ENOMEM`. \mallocs If mem is not set, one calloc, one free. The allocation is unavoidable due to the need to store a type @@ -379,7 +376,7 @@ class LLFIO_DECL async_file_handle : public file_handle LLFIO_MAKE_FREE_FUNCTION template // LLFIO_REQUIRES(detail::is_invocable_r &>::value) // - result async_barrier(io_request reqs, CompletionRoutine &&completion, bool wait_for_device = false, bool and_metadata = false, span mem = {}) noexcept + result async_barrier(io_request reqs, CompletionRoutine &&completion, barrier_kind kind = barrier_kind::nowait_data_only, span mem = {}) noexcept { LLFIO_LOG_FUNCTION_CALL(this); struct completion_handler : _erased_completion_handler @@ -401,15 +398,15 @@ class LLFIO_DECL async_file_handle : public file_handle void *address() noexcept final { return &completion; } } ch{std::forward(completion)}; operation_t operation = operation_t::fsync_sync; - if(!wait_for_device && and_metadata) + if(kind == barrier_kind::nowait_all) { operation = operation_t::fsync_async; } - else if(wait_for_device && !and_metadata) + else if(kind == barrier_kind::wait_data_only) { operation = operation_t::dsync_sync; } - else if(!wait_for_device && !and_metadata) + else if(kind == barrier_kind::nowait_data_only) { operation = operation_t::dsync_async; } @@ -689,35 +686,6 @@ inline result async_temp_inode(io_service &service, const pat { return async_file_handle::async_temp_inode(std::forward(service), std::forward(dir), std::forward(_mode), std::forward(flags)); } -inline async_file_handle::io_result barrier(async_file_handle &self, async_file_handle::io_request reqs = async_file_handle::io_request(), bool wait_for_device = false, bool and_metadata = false, - deadline d = deadline()) noexcept -{ - return self.barrier(std::forward(reqs), std::forward(wait_for_device), std::forward(and_metadata), std::forward(d)); -} -/*! \brief Schedule a barrier to occur asynchronously. - -\note All the caveats and exclusions which apply to `barrier()` also apply here. Note that Microsoft Windows -does not support asynchronously executed barriers, and this call will fail on that operating system. - -\return Either an io_state_ptr to the i/o in progress, or an error code. -\param self The object whose member function to call. -\param reqs A scatter-gather and offset request for what range to barrier. May be ignored on some platforms -which always write barrier the entire file. Supplying a default initialised reqs write barriers the entire file. -\param completion A callable to call upon i/o completion. Spec is `void(async_file_handle *, io_result &)`. -Note that buffers returned may not be buffers input, see documentation for `barrier()`. -\param wait_for_device True if you want the call to wait until data reaches storage and that storage -has acknowledged the data is physically written. Slow. -\param and_metadata True if you want the call to sync the metadata for retrieving the writes before the -barrier after a sudden power loss event. Slow. -\param mem Optional span of memory to use to avoid using `calloc()`. Note span MUST be all bits zero on entry. -\errors As for `barrier()`, plus `ENOMEM`. -\mallocs If mem is not set, one calloc, one free. The allocation is unavoidable due to the need to store a type -erased completion handler of unknown type and state per buffers input. -*/ -template inline result async_barrier(async_file_handle &self, async_file_handle::io_request reqs, CompletionRoutine &&completion, bool wait_for_device = false, bool and_metadata = false, span mem = {}) noexcept -{ - return self.async_barrier(std::forward(reqs), std::forward(completion), std::forward(wait_for_device), std::forward(and_metadata), std::forward(mem)); -} /*! \brief Schedule a read to occur asynchronously. Note that some OS kernels can only process a limited number async i/o diff --git a/include/llfio/v2.0/config.hpp b/include/llfio/v2.0/config.hpp index 692127e0e..52766103a 100644 --- a/include/llfio/v2.0/config.hpp +++ b/include/llfio/v2.0/config.hpp @@ -109,7 +109,7 @@ Distributed under the Boost Software License, Version 1.0. #include <_mingw.h> #endif -#include "quickcpplib/include/cpp_feature.h" +#include "quickcpplib/cpp_feature.h" #ifndef __cpp_exceptions #error LLFIO needs C++ exceptions to be turned on @@ -144,7 +144,7 @@ Distributed under the Boost Software License, Version 1.0. #endif -#include "quickcpplib/include/import.h" +#include "quickcpplib/import.h" #if defined(LLFIO_UNSTABLE_VERSION) && !defined(LLFIO_DISABLE_ABI_PERMUTATION) #include "../revision.hpp" @@ -214,10 +214,10 @@ class file_handle; LLFIO_V2_NAMESPACE_END // Bring in the Boost-lite macros -#include "quickcpplib/include/config.hpp" +#include "quickcpplib/config.hpp" #if LLFIO_LOGGING_LEVEL -#include "quickcpplib/include/ringbuffer_log.hpp" -#include "quickcpplib/include/utils/thread.hpp" +#include "quickcpplib/ringbuffer_log.hpp" +#include "quickcpplib/utils/thread.hpp" #endif // Bring in filesystem #if defined(__has_include) @@ -307,45 +307,45 @@ LLFIO_V2_NAMESPACE_END #endif // Bring in bitfields -#include "quickcpplib/include/bitfield.hpp" +#include "quickcpplib/bitfield.hpp" // Bring in scoped undo -#include "quickcpplib/include/scoped_undo.hpp" +#include "quickcpplib/scoped_undo.hpp" LLFIO_V2_NAMESPACE_BEGIN using QUICKCPPLIB_NAMESPACE::scoped_undo::undoer; LLFIO_V2_NAMESPACE_END // Bring in a span implementation -#include "quickcpplib/include/span.hpp" +#include "quickcpplib/span.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::span; LLFIO_V2_NAMESPACE_END // Bring in an optional implementation -#include "quickcpplib/include/optional.hpp" +#include "quickcpplib/optional.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::optional; LLFIO_V2_NAMESPACE_END // Bring in a byte implementation -#include "quickcpplib/include/byte.hpp" +#include "quickcpplib/byte.hpp" LLFIO_V2_NAMESPACE_BEGIN using QUICKCPPLIB_NAMESPACE::byte::byte; using QUICKCPPLIB_NAMESPACE::byte::to_byte; LLFIO_V2_NAMESPACE_END // Bring in a string_view implementation -#include "quickcpplib/include/string_view.hpp" +#include "quickcpplib/string_view.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::string_view; LLFIO_V2_NAMESPACE_END // Bring in an ensure_flushes implementation -#include "quickcpplib/include/mem_flush_loads_stores.hpp" +#include "quickcpplib/mem_flush_loads_stores.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::mem_flush_loads_stores; LLFIO_V2_NAMESPACE_END // Bring in a detach_cast implementation -#include "quickcpplib/include/detach_cast.hpp" +#include "quickcpplib/detach_cast.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::detach_cast; LLFIO_V2_NAMESPACE_END // Bring in an in_place_detach implementation -#include "quickcpplib/include/in_place_detach_attach.hpp" +#include "quickcpplib/in_place_detach_attach.hpp" LLFIO_V2_NAMESPACE_BEGIN using namespace QUICKCPPLIB_NAMESPACE::in_place_attach_detach; using QUICKCPPLIB_NAMESPACE::in_place_attach_detach::in_place_attach; diff --git a/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp b/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp index 58b554792..7d03cf2f8 100644 --- a/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/async_file_handle.ipp @@ -32,11 +32,11 @@ Distributed under the Boost Software License, Version 1.0. LLFIO_V2_NAMESPACE_BEGIN -async_file_handle::io_result async_file_handle::barrier(async_file_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +async_file_handle::io_result async_file_handle::barrier(async_file_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { LLFIO_LOG_FUNCTION_CALL(this); optional> ret; - OUTCOME_TRY(io_state, async_barrier(reqs, [&ret](async_file_handle *, io_result &&result) { ret = std::move(result); }, wait_for_device, and_metadata)); + OUTCOME_TRY(io_state, async_barrier(reqs, [&ret](async_file_handle *, io_result &&result) { ret = std::move(result); }, kind)); (void) io_state; // While i/o is not done pump i/o completion diff --git a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp index 30278c387..dfa79a29b 100644 --- a/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/directory_handle.ipp @@ -26,15 +26,7 @@ http://www.boost.org/LICENSE_1_0.txt) #include "import.hpp" #ifdef QUICKCPPLIB_ENABLE_VALGRIND -#ifdef __has_include -#if __has_include("../../../quickcpplib/valgrind/memcheck.h") -#include "../../../quickcpplib/valgrind/memcheck.h" -#else -#include "quickcpplib/valgrind/memcheck.h" -#endif -#else -#include "quickcpplib/valgrind/memcheck.h" -#endif +#include "../valgrind/memcheck.h" // from quickcpplib include directory #define LLFIO_VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(a, b) VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE((a), (b)) #else #define LLFIO_VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(a, b) 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 b96232f0e..4f7cd04c0 100644 --- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp @@ -154,17 +154,16 @@ result file_handle::temp_inode(const path_handle &dirh, mode _mode, } } -file_handle::io_result file_handle::barrier(file_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +file_handle::io_result file_handle::barrier(file_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { - (void) wait_for_device; - (void) and_metadata; + (void) kind; LLFIO_LOG_FUNCTION_CALL(this); if(d) { return errc::not_supported; } #ifdef __linux__ - if(!and_metadata) + if(kind == barrier_kind::nowait_data_only || kind == barrier_kind::wait_data_only) { // Linux has a lovely dedicated syscall giving us exactly what we need here extent_type offset = reqs.offset, bytes = 0; @@ -174,7 +173,7 @@ file_handle::io_result file_handle::barrier(fil bytes += req.size(); } unsigned flags = SYNC_FILE_RANGE_WRITE; // start writing all dirty pages in range now - if(wait_for_device) + if(kind == barrier_kind::wait_data_only) { flags |= SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WAIT_AFTER; // block until they're on storage } @@ -185,7 +184,7 @@ file_handle::io_result file_handle::barrier(fil } #endif #if !defined(__FreeBSD__) && !defined(__APPLE__) // neither of these have fdatasync() - if(!and_metadata) + if(kind == barrier_kind::nowait_data_only || kind == barrier_kind::wait_data_only) { if(-1 == ::fdatasync(_v.fd)) { @@ -195,7 +194,7 @@ file_handle::io_result file_handle::barrier(fil } #endif #ifdef __APPLE__ - if(!wait_for_device) + if(kind == barrier_kind::nowait_data_only || kind == barrier_kind::nowait_all) { // OS X fsync doesn't wait for the device to flush its buffers if(-1 == ::fsync(_v.fd)) diff --git a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp index ccb26b2cc..bf843c64e 100644 --- a/include/llfio/v2.0/detail/impl/posix/map_handle.ipp +++ b/include/llfio/v2.0/detail/impl/posix/map_handle.ipp @@ -25,15 +25,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../../../map_handle.hpp" #include "../../../utils.hpp" -#ifdef __has_include -#if __has_include("../../../quickcpplib/include/signal_guard.hpp") -#include "../../../quickcpplib/include/signal_guard.hpp" -#else -#include "quickcpplib/include/signal_guard.hpp" -#endif -#else -#include "quickcpplib/include/signal_guard.hpp" -#endif +#include "quickcpplib/signal_guard.hpp" #include @@ -156,7 +148,7 @@ result map_handle::close() noexcept { if(is_writable() && (_flag & section_handle::flag::barrier_on_close)) { - OUTCOME_TRYV(map_handle::barrier({}, true, false)); + OUTCOME_TRYV(map_handle::barrier({}, barrier_kind::wait_all)); } // printf("%d munmap %p-%p\n", getpid(), _addr, _addr+_reservation); if(-1 == ::munmap(_addr, _reservation)) @@ -181,7 +173,7 @@ native_handle_type map_handle::release() noexcept return {}; } -map_handle::io_result map_handle::barrier(map_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +map_handle::io_result map_handle::barrier(map_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { LLFIO_LOG_FUNCTION_CALL(this); byte *addr = _addr + reqs.offset; @@ -201,7 +193,7 @@ map_handle::io_result map_handle::barrier(map_ha bytes = _length; } // If nvram and not syncing metadata, use lightweight barrier - if(!and_metadata && is_nvram()) + if((kind == barrier_kind::nowait_data_only || kind == barrier_kind::wait_data_only) && is_nvram()) { auto synced = nvram_barrier({addr, bytes}); if(synced.size() >= bytes) @@ -209,16 +201,16 @@ map_handle::io_result map_handle::barrier(map_ha return {reqs.buffers}; } } - int flags = (wait_for_device || and_metadata) ? MS_SYNC : MS_ASYNC; + int flags = (kind != barrier_kind::nowait_data_only) ? MS_SYNC : MS_ASYNC; if(-1 == ::msync(addr, bytes, flags)) { return posix_error(); } // Don't fsync temporary inodes - if((_section->backing() != nullptr) && (wait_for_device || and_metadata)) + if((_section->backing() != nullptr) && kind != barrier_kind::nowait_data_only) { reqs.offset += _offset; - return _section->backing()->barrier(reqs, wait_for_device, and_metadata, d); + return _section->backing()->barrier(reqs, kind, d); } return {reqs.buffers}; } diff --git a/include/llfio/v2.0/detail/impl/posix/utils.ipp b/include/llfio/v2.0/detail/impl/posix/utils.ipp index 2ec36a6f1..8c0338990 100644 --- a/include/llfio/v2.0/detail/impl/posix/utils.ipp +++ b/include/llfio/v2.0/detail/impl/posix/utils.ipp @@ -24,17 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../../../utils.hpp" -#ifdef __has_include -#if __has_include("../../../quickcpplib/include/spinlock.hpp") -#include "../../../quickcpplib/include/spinlock.hpp" -#else -#include "quickcpplib/include/spinlock.hpp" -#endif -#elif __PCPP_ALWAYS_TRUE__ -#include "quickcpplib/include/spinlock.hpp" -#else -#include "../../../quickcpplib/include/spinlock.hpp" -#endif +#include "quickcpplib/spinlock.hpp" #include // for lock_guard diff --git a/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp b/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp index a9dadf5a0..7c7bc80c1 100644 --- a/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp +++ b/include/llfio/v2.0/detail/impl/safe_byte_ranges.ipp @@ -28,21 +28,8 @@ Distributed under the Boost Software License, Version 1.0. #include "../../algorithm/shared_fs_mutex/safe_byte_ranges.hpp" -#ifdef __has_include -#if __has_include("../../quickcpplib/include/uint128.hpp") -#include "../../quickcpplib/include/uint128.hpp" -#include "../../quickcpplib/include/utils/thread.hpp" -#else -#include "quickcpplib/include/uint128.hpp" -#include "quickcpplib/include/utils/thread.hpp" -#endif -#elif __PCPP_ALWAYS_TRUE__ -#include "quickcpplib/include/uint128.hpp" -#include "quickcpplib/include/utils/thread.hpp" -#else -#include "../../quickcpplib/include/uint128.hpp" -#include "../../quickcpplib/include/utils/thread.hpp" -#endif +#include "quickcpplib/uint128.hpp" +#include "quickcpplib/utils/thread.hpp" #include #include diff --git a/include/llfio/v2.0/detail/impl/storage_profile.ipp b/include/llfio/v2.0/detail/impl/storage_profile.ipp index fb2b46ed1..e1c5ec4d5 100644 --- a/include/llfio/v2.0/detail/impl/storage_profile.ipp +++ b/include/llfio/v2.0/detail/impl/storage_profile.ipp @@ -28,17 +28,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../../storage_profile.hpp" #include "../../utils.hpp" -#ifdef __has_include -#if __has_include("../../quickcpplib/include/algorithm/small_prng.hpp") -#include "../../quickcpplib/include/algorithm/small_prng.hpp" -#else -#include "quickcpplib/include/algorithm/small_prng.hpp" -#endif -#elif __PCPP_ALWAYS_TRUE__ -#include "quickcpplib/include/algorithm/small_prng.hpp" -#else -#include "../../quickcpplib/include/algorithm/small_prng.hpp" -#endif +#include "quickcpplib/algorithm/small_prng.hpp" #include #include diff --git a/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp b/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp index b0563f089..ee970a30f 100644 --- a/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/async_file_handle.ipp @@ -27,10 +27,10 @@ Distributed under the Boost Software License, Version 1.0. LLFIO_V2_NAMESPACE_BEGIN -async_file_handle::io_result async_file_handle::barrier(async_file_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +async_file_handle::io_result async_file_handle::barrier(async_file_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { // Pass through the file_handle's implementation, it understands overlapped handles - return file_handle::barrier(reqs, wait_for_device, and_metadata, d); + return file_handle::barrier(reqs, kind, d); } template result async_file_handle::_begin_io(span mem, async_file_handle::operation_t operation, async_file_handle::io_request reqs, async_file_handle::_erased_completion_handler &&completion, IORoutine &&ioroutine) noexcept 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 0462051f2..e8c037567 100644 --- a/include/llfio/v2.0/detail/impl/windows/file_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/file_handle.ipp @@ -294,7 +294,7 @@ result file_handle::temp_inode(const path_handle &dirh, mode _mode, } } -file_handle::io_result file_handle::barrier(file_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +file_handle::io_result file_handle::barrier(file_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { windows_nt_kernel::init(); using namespace windows_nt_kernel; @@ -309,11 +309,11 @@ file_handle::io_result file_handle::barrier(fil auto *isb = reinterpret_cast(&ol); *isb = make_iostatus(); ULONG flags = 0; - if(!wait_for_device && !and_metadata) + if(kind == barrier_kind::nowait_data_only) { - flags = 1 /*FLUSH_FLAGS_FILE_DATA_ONLY*/; + flags = 1 /*FLUSH_FLAGS_FILE_DATA_ONLY*/; // note this doesn't block } - else if(!wait_for_device) + else if(kind == barrier_kind::nowait_all) { flags = 2 /*FLUSH_FLAGS_NO_SYNC*/; } 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 f749d45d4..33ea06d90 100644 --- a/include/llfio/v2.0/detail/impl/windows/map_handle.ipp +++ b/include/llfio/v2.0/detail/impl/windows/map_handle.ipp @@ -26,19 +26,8 @@ Distributed under the Boost Software License, Version 1.0. #include "../../../utils.hpp" #include "import.hpp" -#ifdef __has_include -#if __has_include("../../../quickcpplib/include/signal_guard.hpp") -#include "../../../quickcpplib/include/algorithm/hash.hpp" -#include "../../../quickcpplib/include/signal_guard.hpp" -#else -#include "quickcpplib/include/algorithm/hash.hpp" -#include "quickcpplib/include/signal_guard.hpp" -#endif -#else -#include "quickcpplib/include/algorithm/hash.hpp" -#include "quickcpplib/include/signal_guard.hpp" -#endif - +#include "quickcpplib/algorithm/hash.hpp" +#include "quickcpplib/signal_guard.hpp" LLFIO_V2_NAMESPACE_BEGIN @@ -477,7 +466,7 @@ result map_handle::close() noexcept { if(is_writable() && (_flag & section_handle::flag::barrier_on_close)) { - OUTCOME_TRYV(barrier({}, true, false)); + OUTCOME_TRYV(barrier({}, barrier_kind::wait_all)); } OUTCOME_TRYV(win32_maps_apply(_addr, _reservation, win32_map_sought::committed, [](byte *addr, size_t /* unused */) -> result { NTSTATUS ntstat = NtUnmapViewOfSection(GetCurrentProcess(), addr); @@ -510,7 +499,7 @@ native_handle_type map_handle::release() noexcept return {}; } -map_handle::io_result map_handle::barrier(map_handle::io_request reqs, bool wait_for_device, bool and_metadata, deadline d) noexcept +map_handle::io_result map_handle::barrier(map_handle::io_request reqs, barrier_kind kind, deadline d) noexcept { LLFIO_LOG_FUNCTION_CALL(this); byte *addr = _addr + reqs.offset; @@ -530,7 +519,7 @@ map_handle::io_result map_handle::barrier(map_ha bytes = _reservation - reqs.offset; } // If nvram and not syncing metadata, use lightweight barrier - if(!and_metadata && is_nvram()) + if((kind == barrier_kind::nowait_data_only || kind == barrier_kind::wait_data_only) && is_nvram()) { auto synced = nvram_barrier({addr, bytes}); if(synced.size() >= bytes) @@ -545,10 +534,10 @@ map_handle::io_result map_handle::barrier(map_ha } return success(); })); - if((_section != nullptr) && (_section->backing() != nullptr) && (wait_for_device || and_metadata)) + if((_section != nullptr) && (_section->backing() != nullptr) && kind != barrier_kind::nowait_data_only) { reqs.offset += _offset; - return _section->backing()->barrier(reqs, wait_for_device, and_metadata, d); + return _section->backing()->barrier(reqs, kind, d); } return {reqs.buffers}; } diff --git a/include/llfio/v2.0/detail/impl/windows/utils.ipp b/include/llfio/v2.0/detail/impl/windows/utils.ipp index 2b1e37f8f..717386e14 100644 --- a/include/llfio/v2.0/detail/impl/windows/utils.ipp +++ b/include/llfio/v2.0/detail/impl/windows/utils.ipp @@ -24,7 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../../../utils.hpp" -#include "../../../quickcpplib/include/spinlock.hpp" +#include "quickcpplib/spinlock.hpp" #include "import.hpp" LLFIO_V2_NAMESPACE_BEGIN diff --git a/include/llfio/v2.0/fast_random_file_handle.hpp b/include/llfio/v2.0/fast_random_file_handle.hpp index c5b63d255..1af381dd2 100644 --- a/include/llfio/v2.0/fast_random_file_handle.hpp +++ b/include/llfio/v2.0/fast_random_file_handle.hpp @@ -27,7 +27,7 @@ Distributed under the Boost Software License, Version 1.0. #include "file_handle.hpp" -#include "quickcpplib/include/algorithm/small_prng.hpp" +#include "quickcpplib/algorithm/small_prng.hpp" //! \file fast_random_file_handle.hpp Provides `fast_random_file_handle`. @@ -271,7 +271,7 @@ class LLFIO_DECL fast_random_file_handle : public file_handle } LLFIO_MAKE_FREE_FUNCTION - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), bool /* unused */ = false, bool /* unused */ = false, deadline /* unused */ = deadline()) noexcept override + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), barrier_kind /*unused*/ = barrier_kind::nowait_data_only, deadline /* unused */ = deadline()) noexcept override { OUTCOME_TRY(_perms_check()); // Return null written diff --git a/include/llfio/v2.0/file_handle.hpp b/include/llfio/v2.0/file_handle.hpp index 084c5e78f..b3a0ebc6e 100644 --- a/include/llfio/v2.0/file_handle.hpp +++ b/include/llfio/v2.0/file_handle.hpp @@ -219,7 +219,7 @@ class LLFIO_DECL file_handle : public io_handle, public fs_handle } LLFIO_MAKE_FREE_FUNCTION - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept override; /*! Clone this handle (copy constructor is disabled to avoid accidental copying), optionally race free reopening the handle with different access or caching. @@ -293,9 +293,10 @@ class LLFIO_DECL file_handle : public io_handle, public fs_handle \param bytes The number of bytes to zero. \param d An optional deadline by which the i/o must complete, else it is cancelled. Note function may return significantly after this deadline if the i/o takes long to cancel. - \errors Any of the values POSIX write() can return, `errc::timed_out`, `errc::operation_canceled`. `errc::not_supported` may be - returned if deadline i/o is not possible with this particular handle configuration (e.g. - writing to regular files on POSIX or writing to a non-overlapped HANDLE on Windows). + \errors Any of the values POSIX write() can return, `errc::timed_out`, `errc::operation_canceled`. + `errc::not_supported` may be returned if deadline i/o is not possible with this particular + handle configuration (e.g. writing to regular files on POSIX or writing to a non-overlapped + HANDLE on Windows). \mallocs The default synchronous implementation in file_handle performs no memory allocation. The asynchronous implementation in async_file_handle may perform one calloc and one free. */ diff --git a/include/llfio/v2.0/fs_handle.hpp b/include/llfio/v2.0/fs_handle.hpp index f3b816b61..0104c3123 100644 --- a/include/llfio/v2.0/fs_handle.hpp +++ b/include/llfio/v2.0/fs_handle.hpp @@ -28,7 +28,7 @@ Distributed under the Boost Software License, Version 1.0. #include "path_handle.hpp" #include "path_view.hpp" -#include "quickcpplib/include/uint128.hpp" +#include "quickcpplib/uint128.hpp" //! \file fs_handle.hpp Provides fs_handle @@ -74,7 +74,9 @@ class LLFIO_DECL fs_handle { } //! Implicit move construction of fs_handle permitted - constexpr fs_handle(fs_handle &&o) noexcept : _devid(o._devid), _inode(o._inode) + constexpr fs_handle(fs_handle &&o) noexcept + : _devid(o._devid) + , _inode(o._inode) { o._devid = 0; o._inode = 0; @@ -142,8 +144,8 @@ class LLFIO_DECL fs_handle LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result parent_path_handle(deadline d = std::chrono::seconds(30)) const noexcept; /*! Relinks the current path of this open handle to the new path specified. If `atomic_replace` is - true, the relink \b atomically and silently replaces any item at the new path specified. This operation - is both atomic and silent matching POSIX behaviour even on Microsoft Windows where + true, the relink \b atomically and silently replaces any item at the new path specified. This + operation is both atomic and silent matching POSIX behaviour even on Microsoft Windows where no Win32 API can match POSIX semantics. \warning Some operating systems provide a race free syscall for renaming an open handle (Windows). @@ -168,11 +170,55 @@ class LLFIO_DECL fs_handle LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result relink(const path_handle &base, path_view_type path, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept; - /*! Unlinks the current path of this open handle, causing its entry to immediately disappear from the filing system. - On Windows before Windows 10 1709 unless `flag::win_disable_unlink_emulation` is set, this behaviour is - simulated by renaming the file to something random and setting its delete-on-last-close flag. - Note that Windows may prevent the renaming of a file in use by another process, if so it will - NOT be renamed. +#if 0 // Not implemented yet for absolutely no good reason + /*! Links the inode referred to by this open handle to the path specified. The current path + of this open handle is not changed, unless it has no current path due to being unlinked. + + \warning Some operating systems provide a race free syscall for linking an open handle to a new + location (Linux, Windows). + On all other operating systems this call is \b racy and can result in the wrong inode being + linked. Note that unless `flag::disable_safety_unlinks` is set, this implementation opens a + `path_handle` to the source containing directory first, then checks before linking that the item + about to be hard linked has the same inode as the open file handle. It will retry this matching + until success until the deadline given. This should prevent most unmalicious accidental loss of + data. + + \param base Base for any relative path. + \param path The relative or absolute new path to hard link to. + \param d The deadline by which the matching of the containing directory to the open handle's inode + must succeed, else `errc::timed_out` will be returned. + \mallocs Except on platforms with race free syscalls for renaming open handles (Windows), calls + `current_path()` via `parent_path_handle()` and thus is both expensive and calls malloc many times. + */ + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC + result link(const path_handle &base, path_view_type path, deadline d = std::chrono::seconds(30)) noexcept; + + /*! Clones the inode referenced by the open handle into a new inode referencing the same extents + for the file content, with a copy of the same metadata, apart from ownership which is for the + current user. Changes to either inode are guaranteed to not be seen by the other inode i.e. they + become completely independent, even though they initially reference the same file content extents. + If your filing system supports this call (at the time of writing - Linux: XFS, btrfs, ocfs2, smbfs; + Mac OS: APFS; Windows: ReFS, CIFS), this is a very cheap way of copying even very large files. + Be aware that on Samba/CIFS, rather than erroring out if the storage filesystem doesn't implement + support, this call is implemented by having the remove machine perform the file + copy, which is usually much faster than doing the copy over the network. + + \return A file handle instance referring to the cloned inode. + \param base Base for any relative path. + \param path The relative or absolute new path to clone the inode to. + */ + LLFIO_MAKE_FREE_FUNCTION + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC + result clone_inode(const path_handle &base, + path_view_type path) noexcept; +#endif + + /*! Unlinks the current path of this open handle, causing its entry to immediately disappear + from the filing system. On Windows before Windows 10 1709 unless + `flag::win_disable_unlink_emulation` is set, this behaviour is simulated by renaming the file + to something random and setting its delete-on-last-close flag. Note that Windows may prevent + the renaming of a file in use by another process, if so it will NOT be renamed. After the next handle to that file closes, it will become permanently unopenable by anyone else until the last handle is closed, whereupon the entry will be eventually removed by the operating system. diff --git a/include/llfio/v2.0/handle.hpp b/include/llfio/v2.0/handle.hpp index 26b8ff22a..a99887b0a 100644 --- a/include/llfio/v2.0/handle.hpp +++ b/include/llfio/v2.0/handle.hpp @@ -242,8 +242,8 @@ class LLFIO_DECL handle (deleted) path for file inodes no longer cached by the kernel path cache, LLFIO cannot detect the difference. FreeBSD will also return any path leading to the inode if it is hard linked. FreeBSD does implement path retrieval for directory inodes - correctly however, and see `algorithm::stablized_path` for a handle adapter which - makes use of that. + correctly however, and see `algorithm::cached_parent_handle_adapter` for a handle + adapter which makes use of that. On Linux if `/proc` is not mounted, this call fails with an error. All APIs in LLFIO which require the use of `current_path()` can be told to not use it e.g. `flag::disable_safety_unlinks`. diff --git a/include/llfio/v2.0/io_handle.hpp b/include/llfio/v2.0/io_handle.hpp index c454254d7..7155d8a27 100644 --- a/include/llfio/v2.0/io_handle.hpp +++ b/include/llfio/v2.0/io_handle.hpp @@ -50,6 +50,15 @@ class LLFIO_DECL io_handle : public handle using caching = handle::caching; using flag = handle::flag; + //! The kinds of write reordering barrier which can be performed. + enum class barrier_kind + { + nowait_data_only, //!< Barrier data only, non-blocking. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance. + wait_data_only, //!< Barrier data only, block until it is done. This is highly optimised on NV-DIMM storage, but consider using `nvram_barrier()` for even better performance. + nowait_all, //!< Barrier data and the metadata to retrieve it, non-blocking. + wait_all //!< Barrier data and the metadata to retrieve it, block until it is done. + }; + //! The scatter buffer type used by this handle. Guaranteed to be `TrivialType` and `StandardLayoutType`. //! Try to make address and length 64 byte, or ideally, `page_size()` aligned where possible. struct buffer_type @@ -68,7 +77,11 @@ class LLFIO_DECL io_handle : public handle //! Default constructor buffer_type() = default; //! Constructor - constexpr buffer_type(pointer data, size_type len) noexcept : _data(data), _len(len) {} + constexpr buffer_type(pointer data, size_type len) noexcept + : _data(data) + , _len(len) + { + } buffer_type(const buffer_type &) = default; buffer_type(buffer_type &&) = default; buffer_type &operator=(const buffer_type &) = default; @@ -96,6 +109,7 @@ class LLFIO_DECL io_handle : public handle constexpr const_iterator end() const noexcept { return _data + _len; } //! Returns an iterator to after the end of the buffer constexpr const_iterator cend() const noexcept { return _data + _len; } + private: friend constexpr inline void _check_iovec_match(); pointer _data; @@ -119,9 +133,17 @@ class LLFIO_DECL io_handle : public handle //! Default constructor const_buffer_type() = default; //! Constructor - constexpr const_buffer_type(pointer data, size_type len) noexcept : _data(data), _len(len) {} + constexpr const_buffer_type(pointer data, size_type len) noexcept + : _data(data) + , _len(len) + { + } //! Converting constructor from non-const buffer type - constexpr const_buffer_type(buffer_type b) noexcept : _data(b.data()), _len(b.size()) {} + constexpr const_buffer_type(buffer_type b) noexcept + : _data(b.data()) + , _len(b.size()) + { + } const_buffer_type(const const_buffer_type &) = default; const_buffer_type(const_buffer_type &&) = default; const_buffer_type &operator=(const const_buffer_type &) = default; @@ -149,6 +171,7 @@ class LLFIO_DECL io_handle : public handle constexpr const_iterator end() const noexcept { return _data + _len; } //! Returns an iterator to after the end of the buffer constexpr const_iterator cend() const noexcept { return _data + _len; } + private: pointer _data; size_type _len; @@ -197,7 +220,7 @@ class LLFIO_DECL io_handle : public handle // static_assert(std::is_trivially_move_assignable>::value, "io_request is not trivially move assignable!"); static_assert(std::is_standard_layout>::value, "io_request is not a standard layout type!"); #endif - //! The i/o result type used by this handle. Guaranteed to be `TrivialType` apart from construction.. + //! The i/o result type used by this handle. Guaranteed to be `TrivialType` apart from construction. template struct io_result : public LLFIO_V2_NAMESPACE::result { using Base = LLFIO_V2_NAMESPACE::result; @@ -265,7 +288,10 @@ class LLFIO_DECL io_handle : public handle { } //! Explicit conversion from handle permitted - explicit constexpr io_handle(handle &&o) noexcept : handle(std::move(o)) {} + explicit constexpr io_handle(handle &&o) noexcept + : handle(std::move(o)) + { + } //! Move construction permitted io_handle(io_handle &&) = default; //! No copy construction (use `clone()`) @@ -275,8 +301,8 @@ class LLFIO_DECL io_handle : public handle //! No copy assignment io_handle &operator=(const io_handle &) = delete; - /*! \brief The *maximum* number of buffers which a single read or write syscall can process at a time - for this specific open handle. On POSIX, this is known as `IOV_MAX`. + /*! \brief The *maximum* number of buffers which a single read or write syscall can process at a + time for this specific open handle. On POSIX, this is known as `IOV_MAX`. Note that the actual number of buffers accepted for a read or a write may be significantly lower than this system-defined limit, depending on available resources. The `read()` or `write()` @@ -286,8 +312,11 @@ class LLFIO_DECL io_handle : public handle but other OSs do not. Some OSs guarantee that each i/o syscall has effects atomically visible or not to other i/o, other OSs do not. - Microsoft Windows and OS X does not implement scatter-gather file i/o syscalls. + OS X does not implement scatter-gather file i/o syscalls. Thus this function will always return `1` in that situation. + + Microsoft Windows *may* implement scatter-gather file i/o under very limited circumstances. + Most of the time this function will return `1`. */ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t max_buffers() const noexcept; @@ -350,39 +379,36 @@ class LLFIO_DECL io_handle : public handle return std::move(ret).error(); } - /*! \brief Issue a write reordering barrier such that writes preceding the barrier will reach storage - before writes after this barrier. + /*! \brief Issue a write reordering barrier such that writes preceding the barrier will reach + storage before writes after this barrier. - \warning **Assume that this call is a no-op**. It is not reliably implemented in many common use cases, - for example if your code is running inside a LXC container, or if the user has mounted the filing - system with non-default options. Instead open the handle with `caching::reads` which means that all - writes form a strict sequential order not completing until acknowledged by the storage device. - Filing system can and do use different algorithms to give much better performance with `caching::reads`, - some (e.g. ZFS) spectacularly better. + \warning **Assume that this call is a no-op**. It is not reliably implemented in many common + use cases, for example if your code is running inside a LXC container, or if the user has mounted + the filing system with non-default options. Instead open the handle with `caching::reads` which + means that all writes form a strict sequential order not completing until acknowledged by the + storage device. Filing system can and do use different algorithms to give much better performance + with `caching::reads`, some (e.g. ZFS) spectacularly better. - \warning Let me repeat again: consider this call to be a **hint** to poke the kernel with a stick to - go start to do some work sooner rather than later. **It may be ignored entirely**. + \warning Let me repeat again: consider this call to be a **hint** to poke the kernel with a stick + to go start to do some work sooner rather than later. **It may be ignored entirely**. \warning For portability, you can only assume that barriers write order for a single handle - instance. You cannot assume that barriers write order across multiple handles to the same inode, or - across processes. + instance. You cannot assume that barriers write order across multiple handles to the same inode, + or across processes. \return The buffers barriered, which may not be the buffers input. The size of each scatter-gather buffer is updated with the number of bytes of that buffer barriered. - \param reqs A scatter-gather and offset request for what range to barrier. May be ignored on some platforms - which always write barrier the entire file. Supplying a default initialised reqs write barriers the entire file. - \param wait_for_device True if you want the call to wait until data reaches storage and that storage - has acknowledged the data is physically written. Slow. - \param and_metadata True if you want the call to sync the metadata for retrieving the writes before the - barrier after a sudden power loss event. Slow. Setting this to false enables much faster performance, - especially on non-volatile memory. + \param reqs A scatter-gather and offset request for what range to barrier. May be ignored on + some platforms which always write barrier the entire file. Supplying a default initialised reqs + write barriers the entire file. + \param kind Which kind of write reordering barrier to perform. \param d An optional deadline by which the i/o must complete, else it is cancelled. Note function may return significantly after this deadline if the i/o takes long to cancel. \errors Any of the values POSIX fdatasync() or Windows NtFlushBuffersFileEx() can return. \mallocs None. */ LLFIO_MAKE_FREE_FUNCTION - virtual io_result barrier(io_request reqs = io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept = 0; + virtual io_result barrier(io_request reqs = io_request(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept = 0; /*! \class extent_guard \brief RAII holder a locked extent of bytes in a file. @@ -410,7 +436,14 @@ class LLFIO_DECL io_handle : public handle //! Default constructor constexpr extent_guard() {} // NOLINT //! Move constructor - extent_guard(extent_guard &&o) noexcept : _h(o._h), _offset(o._offset), _length(o._length), _exclusive(o._exclusive) { o.release(); } + extent_guard(extent_guard &&o) noexcept + : _h(o._h) + , _offset(o._offset) + , _length(o._length) + , _exclusive(o._exclusive) + { + o.release(); + } //! Move assign extent_guard &operator=(extent_guard &&o) noexcept { @@ -461,23 +494,25 @@ class LLFIO_DECL io_handle : public handle } }; - /*! \brief Tries to lock the range of bytes specified for shared or exclusive access. Be aware this passes through - the same semantics as the underlying OS call, including any POSIX insanity present on your platform: + /*! \brief Tries to lock the range of bytes specified for shared or exclusive access. Be aware + this passes through the same semantics as the underlying OS call, including any POSIX insanity + present on your platform: - Any fd closed on an inode must release all byte range locks on that inode for all - other fds. If your OS isn't new enough to support the non-insane lock API, `flag::byte_lock_insanity` will be set - in flags() after the first call to this function. + other fds. If your OS isn't new enough to support the non-insane lock API, + `flag::byte_lock_insanity` will be set in flags() after the first call to this function. - Threads replace each other's locks, indeed locks replace each other's locks. You almost cetainly should use your choice of an `algorithm::shared_fs_mutex::*` instead of this as those are more portable and performant. - \warning This is a low-level API which you should not use directly in portable code. Another issue is that - atomic lock upgrade/downgrade, if your platform implements that (you should assume it does not in - portable code), means that on POSIX you need to *release* the old `extent_guard` after creating a new one over the - same byte range, otherwise the old `extent_guard`'s destructor will simply unlock the range entirely. On - Windows however upgrade/downgrade locks overlay, so on that platform you must *not* release the old - `extent_guard`. Look into `algorithm::shared_fs_mutex::safe_byte_ranges` for a portable solution. + \warning This is a low-level API which you should not use directly in portable code. Another + issue is that atomic lock upgrade/downgrade, if your platform implements that (you should assume + it does not in portable code), means that on POSIX you need to *release* the old `extent_guard` + after creating a new one over the same byte range, otherwise the old `extent_guard`'s destructor + will simply unlock the range entirely. On Windows however upgrade/downgrade locks overlay, so on + that platform you must *not* release the old `extent_guard`. Look into + `algorithm::shared_fs_mutex::safe_byte_ranges` for a portable solution. \return An extent guard, the destruction of which will call unlock(). \param offset The offset to lock. Note that on POSIX the top bit is always cleared before use @@ -594,42 +629,6 @@ inline io_handle::io_result write(io_handle &self, io_hand { return self.write(std::forward(offset), std::forward(lst), std::forward(d)); } -/*! \brief Issue a write reordering barrier such that writes preceding the barrier will reach storage -before writes after this barrier. - -\warning **Assume that this call is a no-op**. It is not reliably implemented in many common use cases, -for example if your code is running inside a LXC container, or if the user has mounted the filing -system with non-default options. Instead open the handle with `caching::reads` which means that all -writes form a strict sequential order not completing until acknowledged by the storage device. -Filing system can and do use different algorithms to give much better performance with `caching::reads`, -some (e.g. ZFS) spectacularly better. - -\warning Let me repeat again: consider this call to be a **hint** to poke the kernel with a stick to -go start to do some work sooner rather than later. **It may be ignored entirely**. - -\warning For portability, you can only assume that barriers write order for a single handle -instance. You cannot assume that barriers write order across multiple handles to the same inode, or -across processes. - -\return The buffers barriered, which may not be the buffers input. The size of each scatter-gather -buffer is updated with the number of bytes of that buffer barriered. -\param self The object whose member function to call. -\param reqs A scatter-gather and offset request for what range to barrier. May be ignored on some platforms -which always write barrier the entire file. Supplying a default initialised reqs write barriers the entire file. -\param wait_for_device True if you want the call to wait until data reaches storage and that storage -has acknowledged the data is physically written. Slow. -\param and_metadata True if you want the call to sync the metadata for retrieving the writes before the -barrier after a sudden power loss event. Slow. Setting this to false enables much faster performance, -especially on non-volatile memory. -\param d An optional deadline by which the i/o must complete, else it is cancelled. -Note function may return significantly after this deadline if the i/o takes long to cancel. -\errors Any of the values POSIX fdatasync() or Windows NtFlushBuffersFileEx() can return. -\mallocs None. -*/ -inline io_handle::io_result barrier(io_handle &self, io_handle::io_request reqs = io_handle::io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept -{ - return self.barrier(std::forward(reqs), std::forward(wait_for_device), std::forward(and_metadata), std::forward(d)); -} // END make_free_functions.py LLFIO_V2_NAMESPACE_END diff --git a/include/llfio/v2.0/llfio.hpp b/include/llfio/v2.0/llfio.hpp index a33318af3..3739865fa 100644 --- a/include/llfio/v2.0/llfio.hpp +++ b/include/llfio/v2.0/llfio.hpp @@ -62,27 +62,30 @@ import LLFIO_MODULE_NAME; #include "stat.hpp" #include "utils.hpp" -#ifndef LLFIO_LEAN_AND_MEAN +#ifdef LLFIO_INCLUDE_ASYNC_FILE_HANDLE #include "async_file_handle.hpp" #else #include "file_handle.hpp" #endif #include "directory_handle.hpp" -#include "mapped.hpp" #include "statfs.hpp" -#ifndef LLFIO_LEAN_AND_MEAN +#ifdef LLFIO_INCLUDE_STORAGE_PROFILE #include "storage_profile.hpp" #endif #include "fast_random_file_handle.hpp" #include "symlink_handle.hpp" #include "algorithm/handle_adapter/cached_parent.hpp" -#include "algorithm/handle_adapter/xor.hpp" #include "algorithm/shared_fs_mutex/atomic_append.hpp" #include "algorithm/shared_fs_mutex/byte_ranges.hpp" #include "algorithm/shared_fs_mutex/lock_files.hpp" -#include "algorithm/shared_fs_mutex/memory_map.hpp" #include "algorithm/shared_fs_mutex/safe_byte_ranges.hpp" + +#ifndef LLFIO_EXCLUDE_MAPPED_FILE_HANDLE +#include "mapped.hpp" +#include "algorithm/handle_adapter/xor.hpp" +#include "algorithm/shared_fs_mutex/memory_map.hpp" #include "algorithm/trivial_vector.hpp" +#endif #endif diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp index d4c75b1d7..906764afb 100644 --- a/include/llfio/v2.0/map_handle.hpp +++ b/include/llfio/v2.0/map_handle.hpp @@ -456,7 +456,7 @@ class LLFIO_DECL map_handle : public io_handle //! Releases the mapped view, but does NOT release the native handle. LLFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept override; LLFIO_MAKE_FREE_FUNCTION - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override; + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept override; /*! Map unused memory into view, creating new memory if insufficient unused memory is available. Note that the memory mapped by this call may contain non-zero bits (recycled memory) unless `zeroed` is true. \param bytes How many bytes to map. Typically will be rounded up to a multiple of the page size (see `page_size()`). @@ -730,10 +730,6 @@ inline result close(map_handle &self) noexcept { return self.close(); } -inline map_handle::io_result barrier(map_handle &self, map_handle::io_request reqs = map_handle::io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept -{ - return self.barrier(std::forward(reqs), std::forward(wait_for_device), std::forward(and_metadata), std::forward(d)); -} /*! Create new memory and map it into view. \param bytes How many bytes to create and map. Typically will be rounded up to a multiple of the page size (see `page_size()`) on POSIX, 64Kb on Windows. \param zeroed Set to true if only all bits zeroed memory is wanted. diff --git a/include/llfio/v2.0/mapped_file_handle.hpp b/include/llfio/v2.0/mapped_file_handle.hpp index 969ce864d..41df22534 100644 --- a/include/llfio/v2.0/mapped_file_handle.hpp +++ b/include/llfio/v2.0/mapped_file_handle.hpp @@ -327,7 +327,7 @@ class LLFIO_DECL mapped_file_handle : public file_handle } LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result close() noexcept override; LLFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept override; - LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override { return _mh.barrier(reqs, wait_for_device, and_metadata, d); } + LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result barrier(io_request reqs = io_request(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept override { return _mh.barrier(reqs, kind, d); } result clone(size_type reservation, mode mode_ = mode::unchanged, caching caching_ = caching::unchanged, deadline d = std::chrono::seconds(30)) const noexcept { OUTCOME_TRY(fh, file_handle::clone(mode_, caching_, d)); diff --git a/include/llfio/v2.0/outcome b/include/llfio/v2.0/outcome index 2954530fd..90769c82e 160000 --- a/include/llfio/v2.0/outcome +++ b/include/llfio/v2.0/outcome @@ -1 +1 @@ -Subproject commit 2954530fd4556c59c7e2f9696b42af4a952e9ee7 +Subproject commit 90769c82ee9c1c11eef83d700108dfe3685a1cda diff --git a/include/llfio/v2.0/path_discovery.hpp b/include/llfio/v2.0/path_discovery.hpp index 613a14b9a..c68200b2c 100644 --- a/include/llfio/v2.0/path_discovery.hpp +++ b/include/llfio/v2.0/path_discovery.hpp @@ -99,7 +99,7 @@ namespace path_discovery This is implemented by iterating all of the paths returned by `verified_temporary_directories()` and checking what file system is in use. The following regex is used: - `btrfs|cifs|exfat|ext?|f2fs|hfs|jfs|lxfs|nfs|nilf2|ufs|vfat|xfs|zfs|msdosfs|newnfs|ntfs|smbfs|unionfs|fat|fat32` + `btrfs|cifs|exfat|ext(2|3|4)|f2fs|hfs|jfs|lxfs|nfs|nilf2|ufs|vfat|xfs|zfs|msdosfs|newnfs|ntfs|smbfs|unionfs|fat|fat32` The handle is created during `verified_temporary_directories()` and is statically cached thereafter. */ diff --git a/include/llfio/v2.0/quickcpplib b/include/llfio/v2.0/quickcpplib index f744cf9d0..c035910c0 160000 --- a/include/llfio/v2.0/quickcpplib +++ b/include/llfio/v2.0/quickcpplib @@ -1 +1 @@ -Subproject commit f744cf9d0142596d71c96a82e2b5d0b6855e56bb +Subproject commit c035910c060880c9391ceb6af414bdef632df0cf diff --git a/include/llfio/v2.0/status_code.hpp b/include/llfio/v2.0/status_code.hpp index e9296fc29..03c3c4c93 100644 --- a/include/llfio/v2.0/status_code.hpp +++ b/include/llfio/v2.0/status_code.hpp @@ -55,8 +55,8 @@ as that (a) enables safe header only LLFIO on Windows (b) produces better codege #if LLFIO_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" +#include "outcome/experimental/status_result.hpp" +#include "outcome/try.hpp" LLFIO_V2_NAMESPACE_BEGIN @@ -338,7 +338,9 @@ LLFIO_V2_NAMESPACE_END // Bring in a result implementation based on std::error_code -#include "outcome/include/outcome.hpp" +#include "outcome/result.hpp" +#include "outcome/try.hpp" +#include "outcome/utils.hpp" LLFIO_V2_NAMESPACE_BEGIN @@ -553,7 +555,6 @@ inline void error_info::throw_exception() const } template using result = OUTCOME_V2_NAMESPACE::result; -template using outcome = OUTCOME_V2_NAMESPACE::outcome; using OUTCOME_V2_NAMESPACE::failure; using OUTCOME_V2_NAMESPACE::success; 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 diff --git a/include/llfio/v2.0/storage_profile.hpp b/include/llfio/v2.0/storage_profile.hpp index 902621bbb..80ebf32a9 100644 --- a/include/llfio/v2.0/storage_profile.hpp +++ b/include/llfio/v2.0/storage_profile.hpp @@ -28,10 +28,15 @@ Distributed under the Boost Software License, Version 1.0. #include "io_service.hpp" #if LLFIO_EXPERIMENTAL_STATUS_CODE -#include "outcome/include/outcome/experimental/status_outcome.hpp" +#include "outcome/experimental/status_outcome.hpp" LLFIO_V2_NAMESPACE_EXPORT_BEGIN template using outcome = OUTCOME_V2_NAMESPACE::experimental::status_outcome; LLFIO_V2_NAMESPACE_END +#else +#include "outcome/outcome.hpp" +LLFIO_V2_NAMESPACE_EXPORT_BEGIN +template using outcome = OUTCOME_V2_NAMESPACE::outcome; +LLFIO_V2_NAMESPACE_END #endif #include diff --git a/include/llfio/v2.0/utils.hpp b/include/llfio/v2.0/utils.hpp index 751984a63..370a03a9a 100644 --- a/include/llfio/v2.0/utils.hpp +++ b/include/llfio/v2.0/utils.hpp @@ -30,7 +30,7 @@ Distributed under the Boost Software License, Version 1.0. #endif #include "config.hpp" -#include "quickcpplib/include/algorithm/string.hpp" +#include "quickcpplib/algorithm/string.hpp" //! \file utils.hpp Provides namespace utils diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index f14b4bed8..7e4f63bcc 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -38,7 +38,7 @@ function(make_program program) endfunction() make_program(benchmark-iostreams llfio::hl) -make_program(benchmark-locking llfio::hl) +make_program(benchmark-locking llfio::hl kerneltest::hl) make_program(fs-probe llfio::hl) make_program(illegal-codepoints llfio::hl) make_program(key-value-store llfio::hl) diff --git a/programs/benchmark-iostreams/main.cpp b/programs/benchmark-iostreams/main.cpp index 2592c9205..53606c5fc 100644 --- a/programs/benchmark-iostreams/main.cpp +++ b/programs/benchmark-iostreams/main.cpp @@ -27,11 +27,7 @@ Distributed under the Boost Software License, Version 1.0. #define REGIONSIZE (100 * 1024 * 1024) #include "../../include/llfio/llfio.hpp" -#if __has_include("quickcpplib/include/algorithm/small_prng.hpp") -#include "quickcpplib/include/algorithm/small_prng.hpp" -#else -#include "../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp" -#endif +#include "quickcpplib/algorithm/small_prng.hpp" #include #include @@ -151,7 +147,7 @@ int main() auto th = llfio::file({}, "testfile", llfio::file_handle::mode::write, llfio::file_handle::creation::if_needed).value(); std::vector buffer(REGIONSIZE, 'a'); th.write(0, {{(llfio::byte *) buffer.data(), buffer.size()}}).value(); - th.barrier({}, true, true).value(); + th.barrier({}, llfio::file_handle::barrier_kind::wait_all).value(); } { auto begin = nanoclock(); diff --git a/programs/benchmark-locking/main.cpp b/programs/benchmark-locking/main.cpp index 531ce780c..8eaa204b4 100644 --- a/programs/benchmark-locking/main.cpp +++ b/programs/benchmark-locking/main.cpp @@ -31,7 +31,7 @@ Distributed under the Boost Software License, Version 1.0. #define _CRT_SECURE_NO_WARNINGS 1 #include "../../include/llfio/llfio.hpp" -#include "kerneltest/include/kerneltest/v1.0/child_process.hpp" +#include "kerneltest/v1.0/child_process.hpp" #include #include diff --git a/programs/fs-probe/main.cpp b/programs/fs-probe/main.cpp index 2ef117aeb..db1a11a19 100644 --- a/programs/fs-probe/main.cpp +++ b/programs/fs-probe/main.cpp @@ -23,8 +23,10 @@ Distributed under the Boost Software License, Version 1.0. */ #define _CRT_SECURE_NO_WARNINGS 1 +#define LLFIO_INCLUDE_STORAGE_PROFILE 1 #include "../../include/llfio/llfio.hpp" +#include "outcome/iostream_support.hpp" #include #include diff --git a/programs/key-value-store/include/key_value_store.hpp b/programs/key-value-store/include/key_value_store.hpp index 2740ce06e..8ec30836c 100644 --- a/programs/key-value-store/include/key_value_store.hpp +++ b/programs/key-value-store/include/key_value_store.hpp @@ -26,11 +26,7 @@ Distributed under the Boost Software License, Version 1.0. #define KEY_VALUE_STORE_HPP #include "../../../include/llfio/llfio.hpp" -#if __has_include("quickcpplib/include/algorithm/open_hash_index.hpp") -#include "quickcpplib/include/algorithm/open_hash_index.hpp" -#else -#include "../../../include/llfio/v2.0/quickcpplib/include/algorithm/open_hash_index.hpp" -#endif +#include "quickcpplib/algorithm/open_hash_index.hpp" #include diff --git a/programs/key-value-store/main.cpp b/programs/key-value-store/main.cpp index c7d921a6a..c6de6d089 100644 --- a/programs/key-value-store/main.cpp +++ b/programs/key-value-store/main.cpp @@ -24,6 +24,8 @@ Distributed under the Boost Software License, Version 1.0. #include "include/key_value_store.hpp" +#include + namespace stackoverflow { namespace filesystem = LLFIO_V2_NAMESPACE::filesystem; diff --git a/test/kerneltest b/test/kerneltest index 08da4697d..489ff59aa 160000 --- a/test/kerneltest +++ b/test/kerneltest @@ -1 +1 @@ -Subproject commit 08da4697da766ff20eecc018b2892eb39314adcf +Subproject commit 489ff59aa243c748126961ed9f7fb237af6d4cab diff --git a/test/test_kernel_decl.hpp b/test/test_kernel_decl.hpp index 95fde2b3e..39abd98fa 100644 --- a/test/test_kernel_decl.hpp +++ b/test/test_kernel_decl.hpp @@ -36,14 +36,14 @@ Distributed under the Boost Software License, Version 1.0. #if LLFIO_EXPERIMENTAL_STATUS_CODE #define KERNELTEST_EXPERIMENTAL_STATUS_CODE 1 -#include "outcome/include/outcome/experimental/status-code/include/iostream_support.hpp" +#include "outcome/experimental/status-code/include/iostream_support.hpp" // Used for initialiser list stored results as the erased form is move-only template using il_result = OUTCOME_V2_NAMESPACE::experimental::status_result; #else template using il_result = LLFIO_V2_NAMESPACE::result; #endif -#include "kerneltest/include/kerneltest.hpp" +#include "kerneltest//kerneltest.hpp" #if 0 // Tell KernelTest's outcome how to grok LLFIO's result diff --git a/test/tests/fast_random_file_handle.cpp b/test/tests/fast_random_file_handle.cpp index d528c44fd..60cd34b35 100644 --- a/test/tests/fast_random_file_handle.cpp +++ b/test/tests/fast_random_file_handle.cpp @@ -24,15 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../test_kernel_decl.hpp" -#ifdef __has_include -#if __has_include("../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp") -#include "../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp" -#else -#include "quickcpplib/include/algorithm/small_prng.hpp" -#endif -#else -#include "../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp" -#endif +#include "quickcpplib/algorithm/small_prng.hpp" static inline void TestFastRandomFileHandleWorks() diff --git a/test/tests/handle_adapter_xor.cpp b/test/tests/handle_adapter_xor.cpp index de1d1d914..39a451d5a 100644 --- a/test/tests/handle_adapter_xor.cpp +++ b/test/tests/handle_adapter_xor.cpp @@ -24,16 +24,7 @@ Distributed under the Boost Software License, Version 1.0. #include "../test_kernel_decl.hpp" -#ifdef __has_include -#if __has_include("../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp") -#include "../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp" -#else -#include "quickcpplib/include/algorithm/small_prng.hpp" -#endif -#else -#include "../../../include/llfio/v2.0/quickcpplib/include/algorithm/small_prng.hpp" -#endif - +#include "quickcpplib/algorithm/small_prng.hpp" static inline void TestXorHandleAdapterWorks() {