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>2021-03-06 00:59:22 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2021-03-06 00:59:22 +0300
commit51c6935efd8e358a32e82a98149075c940555635 (patch)
tree3acf1ed2d6a081fe462c7b65866ac5357849294a
parenta74411eddb6401ab884c5f92cccc24b9a64a9e6f (diff)
Fix lots of bugs due to newer kernel 5.8 in Ubuntu 20.04, and OpenZFS v0.8:
1. clone_extents_to() didn't handle partial clones, and now it does. 2. OpenZFS has a bug in SEEK_DATA which caused empty clones, we use a hacky workaround to fix it. 3. mapped_file_handle now fails loudly if it fails to allocate the map due to VMA exhaustion. This closes off a ton of unpleasant surprise in heavily loaded applications. 4. If your Linux kernel was old enough, or you are on ZFS, renaming a mapped_file_handle caused internal state corruption which led to lots of bad things, including `bad_file_descriptor` errors. 5. On Windows, renaming a mapped_file_handle now tears down and restores the maps, as you cannot rename a file with open maps.
-rw-r--r--include/llfio/revision.hpp6
-rw-r--r--include/llfio/v2.0/detail/impl/posix/file_handle.ipp37
-rw-r--r--include/llfio/v2.0/detail/impl/posix/mapped_file_handle.ipp66
-rw-r--r--include/llfio/v2.0/detail/impl/windows/mapped_file_handle.ipp65
-rw-r--r--include/llfio/v2.0/mapped_file_handle.hpp167
-rw-r--r--include/llfio/v2.0/native_handle_type.hpp11
-rw-r--r--test/tests/mapped.cpp1
7 files changed, 276 insertions, 77 deletions
diff --git a/include/llfio/revision.hpp b/include/llfio/revision.hpp
index c3d97eb3..06790036 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 74e710643944764a3548847322d6cb9a4a7ed5ab
-#define LLFIO_PREVIOUS_COMMIT_DATE "2021-02-23 11:27:11 +00:00"
-#define LLFIO_PREVIOUS_COMMIT_UNIQUE 74e71064
+#define LLFIO_PREVIOUS_COMMIT_REF a74411eddb6401ab884c5f92cccc24b9a64a9e6f
+#define LLFIO_PREVIOUS_COMMIT_DATE "2021-02-23 14:33:57 +00:00"
+#define LLFIO_PREVIOUS_COMMIT_UNIQUE a74411ed
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 d36d397a..144f8192 100644
--- a/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
+++ b/include/llfio/v2.0/detail/impl/posix/file_handle.ipp
@@ -581,6 +581,18 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte
for(;;)
{
#ifdef __linux__
+ if(1)
+ {
+ /* There is a bug in ZFSOnLinux where SEEK_DATA returns no data in file
+ if the file was rewritten using a mmap which is then closed, and the file
+ immediately reopened and SEEK_DATA called upon it. If you read a single
+ byte, it seems to kick SEEK_DATA into working correctly.
+
+ See https://github.com/openzfs/zfs/issues/11697 for more.
+ */
+ char b;
+ (void) ::pread(_v.fd, &b, 1, end);
+ }
start = lseek64(_v.fd, end, SEEK_DATA);
#else
start = lseek(_v.fd, end, SEEK_DATA);
@@ -747,17 +759,17 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte
auto _copy_file_range = [&](int infd, off_t *inoffp, int outfd, off_t *outoffp, size_t len, unsigned int flags) -> ssize_t {
#if defined(__linux__)
#if defined __aarch64__
- return syscall(285 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(285 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#elif defined __arm__
- return syscall(391 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(391 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#elif defined __i386__
- return syscall(377 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(377 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#elif defined __powerpc64__
- return syscall(379 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(379 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#elif defined __sparc__
- return syscall(357 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(357 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#elif defined __x86_64__
- return syscall(326 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
+ return syscall(326 /*__NR_copy_file_range*/, infd, inoffp, outfd, outoffp, len, flags);
#else
#error Unknown Linux platform
#endif
@@ -790,23 +802,30 @@ result<file_handle::extent_pair> file_handle::clone_extents_to(file_handle::exte
{
for(extent_type thisoffset = 0; thisoffset < item.src.length; thisoffset += blocksize)
{
+ retry_clone:
bool done = false;
const auto thisblock = std::min(blocksize, item.src.length - thisoffset);
if(duplicate_extents && item.op == workitem::clone_extents)
{
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)
+ auto bytes_cloned = _copy_file_range(_v.fd, &off_in, dest.native_handle().fd, &off_out, thisblock, 0);
+ if(bytes_cloned <= 0)
{
- if((EXDEV != errno && EOPNOTSUPP != errno && ENOSYS != errno) || !emulate_if_unsupported)
+ if(bytes_cloned < 0 && ((EXDEV != errno && EOPNOTSUPP != errno && ENOSYS != errno) || !emulate_if_unsupported))
{
return posix_error();
}
duplicate_extents = false; // emulate using copy of bytes
}
- else
+ else if((size_t) bytes_cloned == thisblock)
{
done = true;
}
+ else
+ {
+ thisoffset += bytes_cloned;
+ goto retry_clone;
+ }
}
if(!done && (item.op == workitem::copy_bytes || (!duplicate_extents && item.op == workitem::clone_extents)))
{
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 da5db0c0..5543b980 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
@@ -1,5 +1,5 @@
/* An mapped handle to a file
-(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
+(C) 2017-2021 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
File Created: Sept 2017
@@ -27,15 +27,20 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_BEGIN
-result<mapped_file_handle::size_type> mapped_file_handle::reserve(size_type reservation) noexcept
+result<mapped_file_handle::size_type> mapped_file_handle::_reserve(extent_type &length, size_type reservation) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- OUTCOME_TRY(auto &&length, underlying_file_maximum_extent());
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ OUTCOME_TRY(length, underlying_file_maximum_extent());
if(length == 0)
{
- // Not portable to map an empty file, so do nothing
+ // Not portable to map an empty file, but retain reservation
_reservation = reservation;
- return success();
}
if(reservation == 0)
{
@@ -70,6 +75,7 @@ result<void> mapped_file_handle::close() noexcept
LLFIO_LOG_FUNCTION_CALL(this);
if(_mh.is_valid())
{
+ assert(_mh.native_handle()._init == native_handle()._init);
OUTCOME_TRYV(_mh.close());
}
if(_sh.is_valid())
@@ -83,6 +89,7 @@ native_handle_type mapped_file_handle::release() noexcept
LLFIO_LOG_FUNCTION_CALL(this);
if(_mh.is_valid())
{
+ assert(_mh.native_handle()._init == native_handle()._init);
(void) _mh.close();
}
if(_sh.is_valid())
@@ -95,6 +102,12 @@ native_handle_type mapped_file_handle::release() noexcept
result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type newsize) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
// Release all maps and sections and truncate the backing file to zero
if(newsize == 0)
{
@@ -142,6 +155,12 @@ result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type
result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcept
{
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
OUTCOME_TRY(auto &&length, underlying_file_maximum_extent());
if(length > _reservation)
{
@@ -156,7 +175,7 @@ result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcep
}
if(!_sh.is_valid())
{
- OUTCOME_TRYV(reserve(_reservation));
+ (void) reserve(_reservation);
return length;
}
// Adjust the map to reflect the new size of the section
@@ -164,4 +183,39 @@ result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcep
return length;
}
+result<void> mapped_file_handle::relink(const path_handle &base, path_view_type path, bool atomic_replace, deadline d) noexcept
+{
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ // On POSIX relink() may change _v.fd sometimes.
+ auto ret = file_handle::relink(base, path, atomic_replace, d);
+ bool restore_maps = false;
+ if(_sh.is_valid() && _v.fd != _sh.native_handle().fd)
+ {
+ OUTCOME_TRY(_sh.close());
+ restore_maps = true;
+ }
+ if(_mh.is_valid() && _v.fd != _mh.native_handle().fd)
+ {
+ OUTCOME_TRY(_mh.close());
+ restore_maps = true;
+ }
+ if(restore_maps)
+ {
+ OUTCOME_TRY(reserve(_reservation));
+ }
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ return ret;
+}
+
+
LLFIO_V2_NAMESPACE_END
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 6afece64..0bdaf4a5 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
@@ -1,5 +1,5 @@
/* An mapped handle to a file
-(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
+(C) 2017-2021 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
File Created: Sept 2017
@@ -27,15 +27,20 @@ Distributed under the Boost Software License, Version 1.0.
LLFIO_V2_NAMESPACE_BEGIN
-result<mapped_file_handle::size_type> mapped_file_handle::reserve(size_type reservation) noexcept
+result<mapped_file_handle::size_type> mapped_file_handle::_reserve(extent_type &length, size_type reservation) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
- OUTCOME_TRY(auto &&length, underlying_file_maximum_extent());
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ OUTCOME_TRY(length, underlying_file_maximum_extent());
if(length == 0)
{
- // Cannot create a section nor a map for a zero lengthed file
+ // Not portable to map an empty file, but retain reservation
_reservation = reservation;
- return success();
}
if(reservation == 0)
{
@@ -80,6 +85,7 @@ result<void> mapped_file_handle::close() noexcept
LLFIO_LOG_FUNCTION_CALL(this);
if(_mh.is_valid())
{
+ assert(_mh.native_handle()._init == native_handle()._init);
OUTCOME_TRYV(_mh.close());
}
if(_sh.is_valid())
@@ -93,6 +99,7 @@ native_handle_type mapped_file_handle::release() noexcept
LLFIO_LOG_FUNCTION_CALL(this);
if(_mh.is_valid())
{
+ assert(_mh.native_handle()._init == native_handle()._init);
(void) _mh.close();
}
if(_sh.is_valid())
@@ -105,6 +112,12 @@ native_handle_type mapped_file_handle::release() noexcept
result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type newsize) noexcept
{
LLFIO_LOG_FUNCTION_CALL(this);
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
// Release all maps and sections and truncate the backing file to zero
if(newsize == 0)
{
@@ -161,6 +174,12 @@ result<mapped_file_handle::extent_type> mapped_file_handle::truncate(extent_type
result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcept
{
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
OUTCOME_TRY(auto &&length, underlying_file_maximum_extent());
if(length > _reservation)
{
@@ -175,7 +194,7 @@ result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcep
}
if(!_sh.is_valid())
{
- OUTCOME_TRYV(reserve(_reservation));
+ (void) reserve(_reservation);
return length;
}
OUTCOME_TRY(auto &&size, _sh.length());
@@ -193,4 +212,38 @@ result<mapped_file_handle::extent_type> mapped_file_handle::update_map() noexcep
return length;
}
+result<void> mapped_file_handle::relink(const path_handle &base, path_view_type path, bool atomic_replace, deadline d) noexcept
+{
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ // On Windows relink() won't succeed unless all maps are torn down
+ bool restore_maps = false;
+ if(_sh.is_valid())
+ {
+ OUTCOME_TRY(_sh.close());
+ restore_maps = true;
+ }
+ if(_mh.is_valid())
+ {
+ OUTCOME_TRY(_mh.close());
+ restore_maps = true;
+ }
+ auto ret = file_handle::relink(base, path, atomic_replace, d);
+ if(restore_maps)
+ {
+ OUTCOME_TRY(reserve(_reservation));
+ }
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
+ return ret;
+}
+
LLFIO_V2_NAMESPACE_END
diff --git a/include/llfio/v2.0/mapped_file_handle.hpp b/include/llfio/v2.0/mapped_file_handle.hpp
index 44b24ec5..14c228c9 100644
--- a/include/llfio/v2.0/mapped_file_handle.hpp
+++ b/include/llfio/v2.0/mapped_file_handle.hpp
@@ -1,5 +1,5 @@
/* An mapped handle to a file
-(C) 2017 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
+(C) 2017-2021 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
File Created: Sept 2017
@@ -62,13 +62,13 @@ Reads always return the original mapped data, and do not fill any buffers passed
For obvious reasons the utility of this class on 32-bit systems is limited,
but can be useful when used with smaller files.
-Note that zero length files cannot be memory mapped, and writes past the maximum
+Note that zero length files cannot be memory mapped on most platforms, and writes past the maximum
extent do NOT auto-extend the size of the file, rather the data written beyond the maximum valid
extent has undefined kernel-specific behaviour, which includes segfaulting. You must therefore always
`truncate(newsize)` to resize the file and its maps before you can read or write to it,
and be VERY careful to not read or write beyond the maximum extent of the file.
-Therefore, when a file is created or is otherwise of zero length, `address()` will return
+On most platforms, when a file is created or is otherwise of zero length, `address()` will return
a null pointer. Similarly, calling `truncate(0)` will close the map and section handles,
they will be recreated on next truncation to a non-zero size.
@@ -81,9 +81,23 @@ file into the beginning of the reservation. The remainder of the pages may be in
and may generate a segfault, or they may automatically reflect any growth in the underlying
file. This is why `read()` and `write()` only know about the reservation size, and will read
and write memory up to that reservation size, without checking if the memory involved exists
-or not yet. You are guaranteed on POSIX only that `address()` will not return a new
-value unless you truncate from a bigger length to a smaller length, or you call `reserve()`
-with a new reservation or `truncate()` with a value bigger than the reservation.
+or not yet.
+
+You are guaranteed on POSIX only that `address()` will not return a new value unless:
+
+1. You truncate from a bigger length to a smaller length.
+2. You call `reserve()` with a new reservation.
+3. You call `truncate()` with a value bigger than the reservation.
+4. You call `relink()` with `atomic_replace = false`, which may on some platforms require
+a close-open file descriptor cycle as part of its implementation.
+
+You are guaranteed on Windows only that `address()` will not return a new value unless:
+
+1. You truncate from a bigger length to a smaller length.
+2. You call `reserve()` with a new reservation.
+3. You call `truncate()` with a value bigger than the reservation.
+4. You call `relink()`, which requires closing and reopening the map because you cannot
+rename a file with an open map on Windows.
`maximum_extent()` in mapped file handle is an alias for `update_map()`. `update_map()`
fetches the maximum extent of the underlying file, and if it has changed from the map's
@@ -103,6 +117,14 @@ mixing mapped and normal i/o is generally safe except at the end of a file where
conditions and outright kernel bugs tend to abound. To avoid these, solely and exclusively
use a dedicated handle configured to atomic append only to do the appends.
+\warning For 64-bit systems under heavy load, or all 32-bit systems, one can run out of
+enough contiguous virtual memory address space to map all of a large file. This generally
+presents itself as an error code comparing equal to `errc::not_enough_memory`, and it can
+appear from the constructor, `truncate()`, `reserve()` and most of the other functions
+in this class not inherited from base classes. `update_map()` never returns
+`errc::not_enough_memory`, but `relink()` may do so, due to the potential map teardown
+and recreate.
+
## Microsoft Windows only
Microsoft Windows can have quite different semantics to POSIX, which are important to be
@@ -161,6 +183,7 @@ protected:
barrier_kind kind = barrier_kind::nowait_data_only,
deadline d = deadline()) noexcept override
{
+ assert(_mh.native_handle()._init == native_handle()._init);
switch(kind)
{
case barrier_kind::nowait_view_only:
@@ -197,18 +220,12 @@ protected:
}
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> _do_read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept override
{
- if(_mh.address() == nullptr)
- {
- OUTCOME_TRY(auto &&length, _sh.length());
- if(length > 0)
- {
- return errc::not_enough_memory; // reserve() failed probably due to VMA exhaustion
- }
- }
+ assert(_mh.native_handle()._init == native_handle()._init);
return _mh.read(reqs, d);
}
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> _do_write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override
{
+ assert(_mh.native_handle()._init == native_handle()._init);
if(!!(_sh.section_flags() & section_handle::flag::write_via_syscall))
{
const auto batch = max_buffers();
@@ -237,17 +254,11 @@ protected:
}
return reqs.buffers;
}
- if(_mh.address() == nullptr)
- {
- OUTCOME_TRY(auto &&length, _sh.length());
- if(length > 0)
- {
- return errc::not_enough_memory; // reserve() failed probably due to VMA exhaustion
- }
- }
return _mh.write(reqs, d);
}
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_type> _reserve(extent_type &length, size_type reservation) noexcept;
+
public:
//! Default constructor
constexpr mapped_file_handle() {} // NOLINT
@@ -259,6 +270,12 @@ public:
, _sh(std::move(o._sh))
, _mh(std::move(o._mh))
{
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
_sh.set_backing(this);
_mh.set_section(&_sh);
}
@@ -275,12 +292,23 @@ public:
: file_handle(std::move(o))
, _sh(sflags)
{
- auto out = reserve(reservation);
+ auto length = (extent_type) -1;
+ auto out = _reserve(length, reservation);
if(!out)
{
+ if(length != 0)
+ {
+ out.value(); // throw
+ }
+ // sink the error as file length is currently zero, which cannot map on some platforms
_reservation = reservation;
- // sink the error
}
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
}
//! Move assignment of mapped_file_handle permitted
mapped_file_handle &operator=(mapped_file_handle &&o) noexcept
@@ -291,6 +319,12 @@ public:
}
this->~mapped_file_handle();
new(this) mapped_file_handle(std::move(o));
+#ifndef NDEBUG
+ if(_mh.is_valid())
+ {
+ assert(_mh.native_handle()._init == native_handle()._init);
+ }
+#endif
return *this;
}
//! No copy assignment
@@ -324,28 +358,35 @@ public:
creation _creation = creation::open_existing, caching _caching = caching::all, flag flags = flag::none,
section_handle::flag sflags = section_handle::flag::none) noexcept
{
- if(_mode == mode::append)
- {
- return errc::invalid_argument;
- }
- OUTCOME_TRY(auto &&fh, file_handle::file(base, _path, _mode, _creation, _caching, flags));
- switch(_creation)
- {
- default:
+ try
{
- // Attempt mapping now (may silently fail if file is empty)
- mapped_file_handle mfh(std::move(fh), reservation, sflags);
- return {std::move(mfh)};
+ if(_mode == mode::append)
+ {
+ return errc::invalid_argument;
+ }
+ OUTCOME_TRY(auto &&fh, file_handle::file(base, _path, _mode, _creation, _caching, flags));
+ switch(_creation)
+ {
+ default:
+ {
+ // Attempt mapping now (may silently fail if file is empty)
+ mapped_file_handle mfh(std::move(fh), reservation, sflags);
+ return {std::move(mfh)};
+ }
+ case creation::only_if_not_exist:
+ case creation::truncate_existing:
+ case creation::always_new:
+ {
+ // Don't attempt mapping now as file will be empty
+ mapped_file_handle mfh(std::move(fh), sflags);
+ mfh._reservation = reservation;
+ return {std::move(mfh)};
+ }
+ }
}
- case creation::only_if_not_exist:
- case creation::truncate_existing:
- case creation::always_new:
+ catch(...)
{
- // Don't attempt mapping now as file will be empty
- mapped_file_handle mfh(std::move(fh), sflags);
- mfh._reservation = reservation;
- return {std::move(mfh)};
- }
+ return error_from_exception();
}
}
//! \overload
@@ -428,10 +469,17 @@ public:
mapped_temp_inode(size_type reservation = 0, const path_handle &dir = path_discovery::storage_backed_temporary_files_directory(), mode _mode = mode::write,
flag flags = flag::none, section_handle::flag sflags = section_handle::flag::none) noexcept
{
- OUTCOME_TRY(auto &&v, file_handle::temp_inode(dir, _mode, flags));
- mapped_file_handle ret(std::move(v), sflags);
- ret._reservation = reservation;
- return {std::move(ret)};
+ try
+ {
+ OUTCOME_TRY(auto &&v, file_handle::temp_inode(dir, _mode, flags));
+ mapped_file_handle ret(std::move(v), sflags);
+ ret._reservation = reservation;
+ return {std::move(ret)};
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
}
//! The memory section this handle is using
@@ -466,7 +514,11 @@ public:
Note that this is an expensive call, and `address()` may return a different value afterwards.
This call will fail if the underlying file has zero length.
*/
- LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_type> reserve(size_type reservation = 0) noexcept;
+ result<size_type> reserve(size_type reservation = 0) noexcept
+ {
+ auto length = (extent_type) -1;
+ return _reserve(length, reservation);
+ }
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~mapped_file_handle() override
{
@@ -480,8 +532,15 @@ public:
result<mapped_file_handle> reopen(size_type reservation, mode mode_ = mode::unchanged, caching caching_ = caching::unchanged,
deadline d = std::chrono::seconds(30)) const noexcept
{
- OUTCOME_TRY(auto &&fh, file_handle::reopen(mode_, caching_, d));
- return mapped_file_handle(std::move(fh), reservation, _sh.section_flags());
+ try
+ {
+ OUTCOME_TRY(auto &&fh, file_handle::reopen(mode_, caching_, d));
+ return mapped_file_handle(std::move(fh), reservation, _sh.section_flags());
+ }
+ catch(...)
+ {
+ return error_from_exception();
+ }
}
LLFIO_DEADLINE_TRY_FOR_UNTIL(reopen)
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_multiplexer(io_multiplexer *c = this_thread::multiplexer()) noexcept override
@@ -532,6 +591,8 @@ public:
If the internal section and map handle are invalid, they are restored unless the underlying file is zero length.
If the size of the underlying file has become zero length, the internal section and map handle are closed.
+
+ This function never returns `errc::not_enough_memory`, even if it calls `reserve()`.
*/
LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<extent_type> update_map() noexcept;
@@ -584,6 +645,12 @@ public:
*/
#endif
using file_handle::write;
+
+ LLFIO_MAKE_FREE_FUNCTION
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC
+ result<void> relink(const path_handle &base, path_view_type path, bool atomic_replace = true, deadline d = std::chrono::seconds(30)) noexcept override;
+
+ LLFIO_DEADLINE_TRY_FOR_UNTIL(relink)
};
//! \brief Constructor for `mapped_file_handle`
diff --git a/include/llfio/v2.0/native_handle_type.hpp b/include/llfio/v2.0/native_handle_type.hpp
index 116ba57d..4dba6ee9 100644
--- a/include/llfio/v2.0/native_handle_type.hpp
+++ b/include/llfio/v2.0/native_handle_type.hpp
@@ -75,7 +75,8 @@ struct native_handle_type // NOLINT
_child_close_executed = 1U << 31U // used to trap when vptr has become corrupted
} QUICKCPPLIB_BITFIELD_END(disposition)
- union {
+ union
+ {
intptr_t _init{-1};
//! A POSIX file descriptor
int fd; // NOLINT
@@ -140,6 +141,11 @@ struct native_handle_type // NOLINT
//! True if invalid
constexpr bool operator!() const noexcept { return !is_valid(); }
+ //! True if equal
+ constexpr bool operator==(const native_handle_type &o) const noexcept { return behaviour == o.behaviour && _init == o._init; }
+ //! True if unequal
+ constexpr bool operator!=(const native_handle_type &o) const noexcept { return behaviour != o.behaviour || _init != o._init; }
+
//! True if the handle is valid
constexpr bool is_valid() const noexcept { return _init != -1 && static_cast<unsigned>(behaviour) != 0; }
@@ -176,7 +182,8 @@ struct native_handle_type // NOLINT
//! True if a memory allocation
constexpr bool is_allocation() const noexcept { return (behaviour & disposition::allocation) ? true : false; }
};
-static_assert((sizeof(void *) == 4 && sizeof(native_handle_type) == 8) || (sizeof(void *) == 8 && sizeof(native_handle_type) == 12), "native_handle_type is not 8 or 12 bytes in size!");
+static_assert((sizeof(void *) == 4 && sizeof(native_handle_type) == 8) || (sizeof(void *) == 8 && sizeof(native_handle_type) == 12),
+ "native_handle_type is not 8 or 12 bytes in size!");
// Not trivially copyable, as has non-trivial move.
static_assert(std::is_trivially_destructible<native_handle_type>::value, "native_handle_type is not trivially destructible!");
static_assert(std::is_trivially_copy_constructible<native_handle_type>::value, "native_handle_type is not trivially copy constructible!");
diff --git a/test/tests/mapped.cpp b/test/tests/mapped.cpp
index d865f71d..f978a372 100644
--- a/test/tests/mapped.cpp
+++ b/test/tests/mapped.cpp
@@ -73,7 +73,6 @@ static inline void TestMappedView2()
filesystem::remove("testfile", ec);
}
mapped_file_handle mfh = mapped_file_handle::mapped_file(1024 * 1024, {}, "testfile", file_handle::mode::write, file_handle::creation::if_needed, file_handle::caching::all, file_handle::flag::unlink_on_first_close).value();
- BOOST_CHECK(mfh.address() == nullptr);
mfh.truncate(10000 * sizeof(int)).value();
byte *addr = mfh.address();
BOOST_CHECK(addr != nullptr);