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>2018-07-03 11:43:55 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-07-03 11:43:55 +0300
commit53d415a3b69d2680c15ae2c56a9baf9b149ad71d (patch)
treeb55e046b3b7a2e504c9abebf4a15a3dddff67eba /include/llfio/v2.0/map_handle.hpp
parent69e9c42365a92bc1af57a843f48513e19aaf0cdf (diff)
Rename afio to llfio part 1 of many
Diffstat (limited to 'include/llfio/v2.0/map_handle.hpp')
-rw-r--r--include/llfio/v2.0/map_handle.hpp748
1 files changed, 748 insertions, 0 deletions
diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp
new file mode 100644
index 00000000..8c66450a
--- /dev/null
+++ b/include/llfio/v2.0/map_handle.hpp
@@ -0,0 +1,748 @@
+/* A handle to a source of mapped memory
+(C) 2016-2018 Niall Douglas <http://www.nedproductions.biz/> (14 commits)
+File Created: August 2016
+
+
+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)
+*/
+
+#ifndef AFIO_MAP_HANDLE_H
+#define AFIO_MAP_HANDLE_H
+
+#include "file_handle.hpp"
+
+//! \file map_handle.hpp Provides `map_handle`
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4251) // dll interface
+#endif
+
+AFIO_V2_NAMESPACE_EXPORT_BEGIN
+
+/*! \class section_handle
+\brief A handle to a source of mapped memory.
+
+There are two configurations of section handle, one where the user supplies the file backing for the
+section, and the other where an internal file descriptor to an unnamed inode in a tmpfs or ramfs based
+temporary directory is kept and managed. The latter is merely a convenience for creating an anonymous
+source of memory which can be resized whilst preserving contents: see `algorithm::trivial_vector<T>`.
+
+On Windows the native handle of this handle is that of the NT kernel section object. On POSIX it is
+a cloned file descriptor of the backing storage if there is backing storage, else it will be the
+aforementioned file descriptor to an unnamed inode.
+*/
+class AFIO_DECL section_handle : public handle
+{
+public:
+ using extent_type = handle::extent_type;
+ using size_type = handle::size_type;
+
+ //! The behaviour of the memory section
+ QUICKCPPLIB_BITFIELD_BEGIN(flag){none = 0U, //!< No flags
+ read = 1U << 0U, //!< Memory views can be read
+ write = 1U << 1U, //!< Memory views can be written
+ cow = 1U << 2U, //!< Memory views can be copy on written
+ execute = 1U << 3U, //!< Memory views can execute code
+
+ nocommit = 1U << 8U, //!< Don't allocate space for this memory in the system immediately
+ prefault = 1U << 9U, //!< Prefault, as if by reading every page, any views of memory upon creation.
+ executable = 1U << 10U, //!< The backing storage is in fact an executable program binary.
+ singleton = 1U << 11U, //!< A single instance of this section is to be shared by all processes using the same backing file.
+
+ barrier_on_close = 1U << 16U, //!< Maps of this section, if writable, issue a `barrier()` when destructed blocking until data (not metadata) reaches physical storage.
+ nvram = 1U << 17U, //!< This section is of non-volatile RAM
+
+ // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
+
+ readwrite = (read | write)};
+ QUICKCPPLIB_BITFIELD_END(flag);
+
+protected:
+ file_handle *_backing{nullptr};
+ file_handle _anonymous;
+ flag _flag{flag::none};
+
+public:
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~section_handle() override;
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override;
+ //! Default constructor
+ constexpr section_handle() {} // NOLINT
+ //! Construct a section handle using the given native handle type for the section and the given i/o handle for the backing storage
+ explicit section_handle(native_handle_type sectionh, file_handle *backing, file_handle anonymous, flag __flag)
+ : handle(sectionh, handle::caching::all)
+ , _backing(backing)
+ , _anonymous(std::move(anonymous))
+ , _flag(__flag)
+ {
+ }
+ //! Implicit move construction of section_handle permitted
+ constexpr section_handle(section_handle &&o) noexcept : handle(std::move(o)), _backing(o._backing), _anonymous(std::move(o._anonymous)), _flag(o._flag)
+ {
+ o._backing = nullptr;
+ o._flag = flag::none;
+ }
+ //! No copy construction (use `clone()`)
+ section_handle(const section_handle &) = delete;
+ //! Move assignment of section_handle permitted
+ section_handle &operator=(section_handle &&o) noexcept
+ {
+ this->~section_handle();
+ new(this) section_handle(std::move(o));
+ return *this;
+ }
+ //! No copy assignment
+ section_handle &operator=(const section_handle &) = delete;
+ //! Swap with another instance
+ AFIO_MAKE_FREE_FUNCTION
+ void swap(section_handle &o) noexcept
+ {
+ section_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ /*! \brief Create a memory section backed by a file.
+ \param backing The handle to use as backing storage.
+ \param maximum_size The initial size of this section, which cannot be larger than any backing file. Zero means to use `backing.maximum_extent()`.
+ \param _flag How to create the section.
+
+ \errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<section_handle> section(file_handle &backing, extent_type maximum_size, flag _flag) noexcept;
+ /*! \brief Create a memory section backed by a file.
+ \param backing The handle to use as backing storage.
+ \param bytes The initial size of this section, which cannot be larger than any backing file. Zero means to use `backing.maximum_extent()`.
+
+ This convenience overload create a writable section if the backing file is writable, otherwise a read-only section.
+
+ \errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static result<section_handle> section(file_handle &backing, extent_type bytes = 0) noexcept { return section(backing, bytes, backing.is_writable() ? (flag::readwrite) : (flag::read)); }
+ /*! \brief Create a memory section backed by an anonymous, managed file.
+ \param bytes The initial size of this section. Cannot be zero.
+ \param dirh Where to create the anonymous, managed file.
+ \param _flag How to create the section.
+
+ \errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<section_handle> section(extent_type bytes, const path_handle &dirh = path_discovery::storage_backed_temporary_files_directory(), flag _flag = flag::read | flag::write) noexcept;
+
+ //! Returns the memory section's flags
+ flag section_flags() const noexcept { return _flag; }
+ //! True if the section reflects non-volatile RAM
+ bool is_nvram() const noexcept { return !!(_flag & flag::nvram); }
+ //! Returns the borrowed handle backing this section, if any
+ file_handle *backing() const noexcept { return _backing; }
+ //! Sets the borrowed handle backing this section, if any
+ void set_backing(file_handle *fh) noexcept { _backing = fh; }
+ //! Returns the borrowed native handle backing this section
+ native_handle_type backing_native_handle() const noexcept { return _backing != nullptr ? _backing->native_handle() : native_handle_type(); }
+ //! Return the current length of the memory section.
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<extent_type> length() const noexcept;
+
+ /*! Resize the current maximum permitted extent of the memory section to the given extent.
+ \param newsize The new size of the memory section, which cannot be zero. Specify zero to use `backing.maximum_extent()`.
+ This cannot exceed the size of any backing file used if that file is not writable.
+
+ \errors Any of the values `NtExtendSection()` or `ftruncate()` can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<extent_type> truncate(extent_type newsize = 0) noexcept;
+};
+inline std::ostream &operator<<(std::ostream &s, const section_handle::flag &v)
+{
+ std::string temp;
+ if(!!(v & section_handle::flag::read))
+ {
+ temp.append("read|");
+ }
+ if(!!(v & section_handle::flag::write))
+ {
+ temp.append("write|");
+ }
+ if(!!(v & section_handle::flag::cow))
+ {
+ temp.append("cow|");
+ }
+ if(!!(v & section_handle::flag::execute))
+ {
+ temp.append("execute|");
+ }
+ if(!!(v & section_handle::flag::nocommit))
+ {
+ temp.append("nocommit|");
+ }
+ if(!!(v & section_handle::flag::prefault))
+ {
+ temp.append("prefault|");
+ }
+ if(!!(v & section_handle::flag::executable))
+ {
+ temp.append("executable|");
+ }
+ if(!!(v & section_handle::flag::singleton))
+ {
+ temp.append("singleton|");
+ }
+ if(!!(v & section_handle::flag::barrier_on_close))
+ {
+ temp.append("barrier_on_close|");
+ }
+ if(!!(v & section_handle::flag::nvram))
+ {
+ temp.append("nvram|");
+ }
+ if(!temp.empty())
+ {
+ temp.resize(temp.size() - 1);
+ if(std::count(temp.cbegin(), temp.cend(), '|') > 0)
+ {
+ temp = "(" + temp + ")";
+ }
+ }
+ else
+ {
+ temp = "none";
+ }
+ return s << "afio::section_handle::flag::" << temp;
+}
+
+//! \brief Constructor for `section_handle`
+template <> struct construct<section_handle>
+{
+ file_handle &backing;
+ section_handle::extent_type maximum_size = 0;
+ section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write;
+ result<section_handle> operator()() const noexcept { return section_handle::section(backing, maximum_size, _flag); }
+};
+
+class mapped_file_handle;
+
+/*! \class map_handle
+\brief A handle to a memory mapped region of memory, either backed by the system page file or by a section.
+
+An important concept to realise with mapped regions is that they can far exceed the size of their backing
+storage. This allows one to reserve address space for a file which may grow in the future. This is how
+`mapped_file_handle` is implemented to provide very fast memory mapped file i/o of a potentially growing
+file.
+
+The size you specify when creating the map handle is the address space reservation. The map's `length()`
+will return the last known **valid** length of the mapped data i.e. the backing storage's length at the
+time of construction. This length is used by `read()` and `write()` to prevent reading and writing off
+the end of the mapped region. You can update this length to the backing storage's length using `update_map()`
+up to the reservation limit.
+
+You can attempt to modify the address space reservation after creation using `truncate()`. If successful,
+this will be more efficient than tearing down the map and creating a new larger map.
+
+The native handle returned by this map handle is always that of the backing storage, but closing this handle
+does not close that of the backing storage, nor does releasing this handle release that of the backing storage.
+Locking byte ranges of this handle is therefore equal to locking byte ranges in the original backing storage,
+which can be very useful.
+
+## Barriers:
+
+`map_handle`, because it implements `io_handle`, implements `barrier()` in a very conservative way
+to account for OS differences i.e. it calls `msync()`, and then the `barrier()` implementation for the backing file
+(probably `fsync()` or equivalent on most platforms, which synchronises the entire file).
+
+This is vast overkill if you are using non-volatile RAM, so a special *inlined* `barrier()` implementation
+taking a single buffer and no other arguments is also provided. This calls the appropriate architecture-specific
+instructions to cause the CPU to write all preceding writes out of the write buffers and CPU caches to main
+memory, so for Intel CPUs this would be `CLWB <each cache line>; SFENCE;`. As this is inlined, it ought to
+produce optimal code. If your CPU does not support the requisite instructions (or AFIO has not added support),
+and empty buffer will be returned to indicate that nothing was barriered, same as the normal `barrier()`
+function.
+
+\sa `mapped_file_handle`, `algorithm::mapped_span`
+*/
+class AFIO_DECL map_handle : public io_handle
+{
+ friend class mapped_file_handle;
+
+public:
+ using extent_type = io_handle::extent_type;
+ using size_type = io_handle::size_type;
+ using mode = io_handle::mode;
+ using creation = io_handle::creation;
+ using caching = io_handle::caching;
+ using flag = io_handle::flag;
+ using buffer_type = io_handle::buffer_type;
+ using const_buffer_type = io_handle::const_buffer_type;
+ using buffers_type = io_handle::buffers_type;
+ using const_buffers_type = io_handle::const_buffers_type;
+ template <class T> using io_request = io_handle::io_request<T>;
+ template <class T> using io_result = io_handle::io_result<T>;
+
+protected:
+ section_handle *_section{nullptr};
+ byte *_addr{nullptr};
+ extent_type _offset{0};
+ size_type _reservation{0}, _length{0};
+ section_handle::flag _flag{section_handle::flag::none};
+
+ explicit map_handle(section_handle *section)
+ : _section(section)
+ , _flag(section != nullptr ? section->section_flags() : section_handle::flag::none)
+ {
+ }
+
+public:
+ //! Default constructor
+ constexpr map_handle() {} // NOLINT
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~map_handle() override;
+ //! Implicit move construction of map_handle permitted
+ constexpr map_handle(map_handle &&o) noexcept : io_handle(std::move(o)), _section(o._section), _addr(o._addr), _offset(o._offset), _reservation(o._reservation), _length(o._length), _flag(o._flag)
+ {
+ o._section = nullptr;
+ o._addr = nullptr;
+ o._offset = 0;
+ o._reservation = 0;
+ o._length = 0;
+ o._flag = section_handle::flag::none;
+ }
+ //! No copy construction (use `clone()`)
+ map_handle(const map_handle &) = delete;
+ //! Move assignment of map_handle permitted
+ map_handle &operator=(map_handle &&o) noexcept
+ {
+ this->~map_handle();
+ new(this) map_handle(std::move(o));
+ return *this;
+ }
+ //! No copy assignment
+ map_handle &operator=(const map_handle &) = delete;
+ //! Swap with another instance
+ AFIO_MAKE_FREE_FUNCTION
+ void swap(map_handle &o) noexcept
+ {
+ map_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ //! Unmap the mapped view.
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept override;
+ //! Releases the mapped view, but does NOT release the native handle.
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept override;
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept override;
+ /*! Lightweight inlined barrier which causes the CPU to write out all buffered writes and dirty cache lines
+ in the request to main memory.
+ \return The cache lines actually barriered. This may be empty. This function does not return an error.
+ \param req The range of cache lines to write barrier.
+ \param evict Whether to also evict the cache lines from CPU caches, useful if they will not be used again.
+
+ Upon return, one knows that memory in the returned buffer has been barriered
+ (it may be empty if there is no support for this operation in AFIO, or if the current CPU does not
+ support this operation). You may find the `is_nvram()` observer of particular use here.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static const_buffer_type barrier(const_buffer_type req, bool evict = false) noexcept
+ {
+ const_buffer_type ret{(const_buffer_type::pointer)(((uintptr_t) req.data) & 31), 0};
+ ret.len = req.data + req.len - ret.data;
+ for(const_buffer_type::pointer addr = ret.data; addr < ret.data + ret.len; addr += 32)
+ {
+ // Slightly UB ...
+ auto *p = reinterpret_cast<const persistent<byte> *>(addr);
+ if(memory_flush_none == p->flush(evict ? memory_flush_evict : memory_flush_retain))
+ {
+ req.len = 0;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ /*! 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 `utils::page_sizes()`) on POSIX, 64Kb on Windows.
+ \param _flag The permissions with which to map the view. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory.
+
+ \note On Microsoft Windows this constructor uses the faster VirtualAlloc() which creates less versatile page backed memory. If you want anonymous memory
+ allocated from a paging file backed section instead, create a page file backed section and then a mapped view from that using
+ the other constructor. This makes available all those very useful VM tricks Windows can do with section mapped memory which
+ VirtualAlloc() memory cannot do.
+
+ \errors Any of the values POSIX mmap() or VirtualAlloc() can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> map(size_type bytes, section_handle::flag _flag = section_handle::flag::readwrite) noexcept;
+
+ /*! Create a memory mapped view of a backing storage, optionally reserving additional address space for later growth.
+ \param section A memory section handle specifying the backing storage to use.
+ \param bytes How many bytes to reserve (0 = the size of the section). Rounded up to nearest 64Kb on Windows.
+ \param offset The offset into the backing storage to map from. Typically needs to be at least a multiple of the page size (see utils::page_sizes()), on Windows it needs to be a multiple of the kernel memory allocation granularity (typically 64Kb).
+ \param _flag The permissions with which to map the view which are constrained by the permissions of the memory section. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory.
+
+ \errors Any of the values POSIX mmap() or NtMapViewOfSection() can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> map(section_handle &section, size_type bytes = 0, extent_type offset = 0, section_handle::flag _flag = section_handle::flag::readwrite) noexcept;
+
+ //! The memory section this handle is using
+ section_handle *section() const noexcept { return _section; }
+ //! Sets the memory section this handle is using
+ void set_section(section_handle *s) noexcept { _section = s; }
+
+ //! The address in memory where this mapped view resides
+ byte *address() const noexcept { return _addr; }
+
+ //! The offset of the memory map.
+ extent_type offset() const noexcept { return _offset; }
+
+ //! The reservation size of the memory map.
+ size_type capacity() const noexcept { return _reservation; }
+
+ //! The size of the memory map. This is the accessible size, NOT the reservation size.
+ AFIO_MAKE_FREE_FUNCTION
+ size_type length() const noexcept { return _length; }
+
+ //! True if the map is of non-volatile RAM
+ bool is_nvram() const noexcept { return !!(_flag & section_handle::flag::nvram); }
+
+ //! Update the size of the memory map to that of any backing section, up to the reservation limit.
+ result<size_type> update_map() noexcept
+ {
+ if(_section == nullptr)
+ {
+ return _reservation;
+ }
+ OUTCOME_TRY(length, _section->length()); // length of the backing file
+ length -= _offset;
+ if(length > _reservation)
+ {
+ length = _reservation;
+ }
+ _length = static_cast<size_type>(length);
+ return _length;
+ }
+
+ /*! Resize the reservation of the memory map without changing the address (unless the map
+ was zero sized, in which case a new address will be chosen).
+
+ If shrinking, address space is released on POSIX, and on Windows if the new size is zero.
+ If the new size is zero, the address is set to null to prevent surprises.
+ Windows does not support modifying existing mapped regions, so if the new size is not
+ zero, the call will probably fail. Windows should let you truncate a previous extension
+ however, if it is exact.
+
+ If expanding, an attempt is made to map in new reservation immediately after the current address
+ reservation, thus extending the reservation. If anything else is mapped in after
+ the current reservation, the function fails.
+
+ \note On all supported platforms apart from OS X, proprietary flags exist to avoid
+ performing a map if a map extension cannot be immediately placed after the current map. On OS X,
+ we hint where we'd like the new map to go, but if something is already there OS X will
+ place the map elsewhere. In this situation, we delete the new map and return failure,
+ which is inefficient, but there is nothing else we can do.
+
+ \return The bytes actually reserved.
+ \param newsize The bytes to truncate the map reservation to. Rounded up to the nearest page size (POSIX) or 64Kb on Windows.
+ \param permit_relocation Permit the address to change (some OSs provide a syscall for resizing
+ a memory map).
+ \errors Any of the values POSIX `mremap()`, `mmap(addr)` or `VirtualAlloc(addr)` can return.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_type> truncate(size_type newsize, bool permit_relocation = false) noexcept;
+
+ //! Ask the system to commit the system resources to make the memory represented by the buffer available with the given permissions. addr and length should be page aligned (see utils::page_sizes()), if not the returned buffer is the region actually committed.
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<buffer_type> commit(buffer_type region, section_handle::flag flag = section_handle::flag::readwrite) noexcept;
+
+ //! Ask the system to make the memory represented by the buffer unavailable and to decommit the system resources representing them. addr and length should be page aligned (see utils::page_sizes()), if not the returned buffer is the region actually decommitted.
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<buffer_type> decommit(buffer_type region) noexcept;
+
+ /*! Zero the memory represented by the buffer. Differs from zero() because it acts on mapped memory, but may call zero() internally.
+
+ On Linux, Windows and FreeBSD any full 4Kb pages will be deallocated from the
+ system entirely, including the extents for them in any backing storage. On newer Linux kernels the kernel can additionally swap whole 4Kb pages for
+ freshly zeroed ones making this a very efficient way of zeroing large ranges of memory.
+ \errors Any of the errors returnable by madvise() or DiscardVirtualMemory or the zero() function.
+ */
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<void> zero_memory(buffer_type region) noexcept;
+
+ /*! Ask the system to unset the dirty flag for the memory represented by the buffer. This will prevent any changes not yet sent to the backing storage from being sent in the future, also if the system kicks out this page and reloads it you may see some edition of the underlying storage instead of what was here. addr
+ and length should be page aligned (see utils::page_sizes()), if not the returned buffer is the region actually undirtied.
+
+ \warning This function destroys the contents of unwritten pages in the region in a totally unpredictable fashion. Only use it if you don't care how much of
+ the region reaches physical storage or not. Note that the region is not necessarily zeroed, and may be randomly zeroed.
+
+ \note Microsoft Windows does not support unsetting the dirty flag on file backed maps, so on Windows this call does nothing.
+ */
+ AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<buffer_type> do_not_store(buffer_type region) noexcept;
+
+ //! Ask the system to begin to asynchronously prefetch the span of memory regions given, returning the regions actually prefetched. Note that on Windows 7 or earlier the system call to implement this was not available, and so you will see an empty span returned.
+ static AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<span<buffer_type>> prefetch(span<buffer_type> regions) noexcept;
+ //! \overload
+ static result<buffer_type> prefetch(buffer_type region) noexcept
+ {
+ OUTCOME_TRY(ret, prefetch(span<buffer_type>(&region, 1)));
+ return *ret.data();
+ }
+
+ /*! \brief Read data from the mapped view.
+
+ \note Because this implementation never copies memory, you can pass in buffers with a null address. As this
+ function never reads any memory, no attempt to trap signal raises can be made, this falls onto the user of
+ this function. See `QUICKCPPLIB_NAMESPACE::signal_guard` for a helper function.
+
+ \return The buffers read, which will never be the buffers input, because they will point into the mapped view.
+ The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred.
+ \param reqs A scatter-gather and offset request.
+ \param d Ignored.
+ \errors None, though the various signals and structured exception throws common to using memory maps may occur.
+ \mallocs None.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept override;
+ using io_handle::read;
+
+ /*! \brief Write data to the mapped view.
+
+ \note This call traps signals and structured exception throws using `QUICKCPPLIB_NAMESPACE::signal_guard`.
+ Instantiating a `QUICKCPPLIB_NAMESPACE::signal_guard_install` somewhere much higher up in the call stack
+ will improve performance enormously. The signal guard may cost less than 100 CPU cycles depending on how
+ you configure it. If you don't want the guard, you can write memory directly using `address()`.
+
+ \return The buffers written, which will never be the buffers input because they will point at where the data was copied into the mapped view.
+ The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred.
+ \param reqs A scatter-gather and offset request.
+ \param d Ignored.
+ \errors If during the attempt to write the buffers to the map a `SIGBUS` or `EXCEPTION_IN_PAGE_ERROR` is raised,
+ an error code comparing equal to `errc::no_space_on_device` will be returned. This may not always be the cause
+ of the raised signal, but it is by far the most likely.
+ \mallocs None if a `QUICKCPPLIB_NAMESPACE::signal_guard_install` is already instanced.
+ */
+ AFIO_MAKE_FREE_FUNCTION
+ AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override;
+ using io_handle::write;
+};
+
+//! \brief Constructor for `map_handle`
+template <> struct construct<map_handle>
+{
+ section_handle &section;
+ map_handle::size_type bytes = 0;
+ map_handle::extent_type offset = 0;
+ section_handle::flag _flag = section_handle::flag::readwrite;
+ result<map_handle> operator()() const noexcept { return map_handle::map(section, bytes, offset, _flag); }
+};
+
+// BEGIN make_free_functions.py
+//! Swap with another instance
+inline void swap(section_handle &self, section_handle &o) noexcept
+{
+ return self.swap(std::forward<decltype(o)>(o));
+}
+/*! \brief Create a memory section backed by a file.
+\param backing The handle to use as backing storage.
+\param maximum_size The initial size of this section, which cannot be larger than any backing file. Zero means to use `backing.maximum_extent()`.
+\param _flag How to create the section.
+
+\errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+*/
+inline result<section_handle> section(file_handle &backing, section_handle::extent_type maximum_size, section_handle::flag _flag) noexcept
+{
+ return section_handle::section(std::forward<decltype(backing)>(backing), std::forward<decltype(maximum_size)>(maximum_size), std::forward<decltype(_flag)>(_flag));
+}
+/*! \brief Create a memory section backed by a file.
+\param backing The handle to use as backing storage.
+\param bytes The initial size of this section, which cannot be larger than any backing file. Zero means to use `backing.maximum_extent()`.
+
+This convenience overload create a writable section if the backing file is writable, otherwise a read-only section.
+
+\errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+*/
+inline result<section_handle> section(file_handle &backing, section_handle::extent_type bytes = 0) noexcept
+{
+ return section_handle::section(std::forward<decltype(backing)>(backing), std::forward<decltype(bytes)>(bytes));
+}
+/*! \brief Create a memory section backed by an anonymous, managed file.
+\param bytes The initial size of this section. Cannot be zero.
+\param dirh Where to create the anonymous, managed file.
+\param _flag How to create the section.
+
+\errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
+*/
+inline result<section_handle> section(section_handle::extent_type bytes, const path_handle &dirh = path_discovery::storage_backed_temporary_files_directory(), section_handle::flag _flag = section_handle::flag::read | section_handle::flag::write) noexcept
+{
+ return section_handle::section(std::forward<decltype(bytes)>(bytes), std::forward<decltype(dirh)>(dirh), std::forward<decltype(_flag)>(_flag));
+}
+//! Return the current maximum permitted extent of the memory section.
+inline result<section_handle::extent_type> length(const section_handle &self) noexcept
+{
+ return self.length();
+}
+/*! Resize the current maximum permitted extent of the memory section to the given extent.
+\param self The object whose member function to call.
+\param newsize The new size of the memory section, which cannot be zero. Specify zero to use `backing.maximum_extent()`.
+This cannot exceed the size of any backing file used if that file is not writable.
+
+\errors Any of the values `NtExtendSection()` or `ftruncate()` can return.
+*/
+inline result<section_handle::extent_type> truncate(section_handle &self, section_handle::extent_type newsize = 0) noexcept
+{
+ return self.truncate(std::forward<decltype(newsize)>(newsize));
+}
+//! Swap with another instance
+inline void swap(map_handle &self, map_handle &o) noexcept
+{
+ return self.swap(std::forward<decltype(o)>(o));
+}
+//! Unmap the mapped view.
+inline result<void> close(map_handle &self) noexcept
+{
+ return self.close();
+}
+inline map_handle::io_result<map_handle::const_buffers_type> barrier(map_handle &self, map_handle::io_request<map_handle::const_buffers_type> reqs = map_handle::io_request<map_handle::const_buffers_type>(), bool wait_for_device = false, bool and_metadata = false, deadline d = deadline()) noexcept
+{
+ return self.barrier(std::forward<decltype(reqs)>(reqs), std::forward<decltype(wait_for_device)>(wait_for_device), std::forward<decltype(and_metadata)>(and_metadata), std::forward<decltype(d)>(d));
+}
+/*! Lightweight inlined barrier which causes the CPU to write out all buffered writes and dirty cache lines
+in the request to main memory.
+\return The cache lines actually barriered. This may be empty. This function does not return an error.
+\param self The object whose member function to call.
+\param req The range of cache lines to write barrier.
+\param evict Whether to also evict the cache lines from CPU caches, useful if they will not be used again.
+
+Upon return, one knows that memory in the returned buffer has been barriered
+(it may be empty if there is no support for this operation in AFIO, or if the current CPU does not
+support this operation). You may find the `is_nvram()` observer of particular use here.
+*/
+inline map_handle::const_buffer_type barrier(map_handle &self, map_handle::const_buffer_type req, bool evict = false) noexcept
+{
+ return self.barrier(std::forward<decltype(req)>(req), std::forward<decltype(evict)>(evict));
+}
+/*! 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 `utils::page_sizes()`) on POSIX, 64Kb on Windows.
+\param _flag The permissions with which to map the view. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory.
+
+\note On Microsoft Windows this constructor uses the faster VirtualAlloc() which creates less versatile page backed memory. If you want anonymous memory
+allocated from a paging file backed section instead, create a page file backed section and then a mapped view from that using
+the other constructor. This makes available all those very useful VM tricks Windows can do with section mapped memory which
+VirtualAlloc() memory cannot do.
+
+\errors Any of the values POSIX mmap() or VirtualAlloc() can return.
+*/
+inline result<map_handle> map(map_handle::size_type bytes, section_handle::flag _flag = section_handle::flag::readwrite) noexcept
+{
+ return map_handle::map(std::forward<decltype(bytes)>(bytes), std::forward<decltype(_flag)>(_flag));
+}
+/*! Create a memory mapped view of a backing storage, optionally reserving additional address space for later growth.
+\param section A memory section handle specifying the backing storage to use.
+\param bytes How many bytes to reserve (0 = the size of the section). Rounded up to nearest 64Kb on Windows.
+\param offset The offset into the backing storage to map from. Typically needs to be at least a multiple of the page size (see utils::page_sizes()), on Windows it needs to be a multiple of the kernel memory allocation granularity (typically 64Kb).
+\param _flag The permissions with which to map the view which are constrained by the permissions of the memory section. `flag::none` can be useful for reserving virtual address space without committing system resources, use commit() to later change availability of memory.
+
+\errors Any of the values POSIX mmap() or NtMapViewOfSection() can return.
+*/
+inline result<map_handle> map(section_handle &section, map_handle::size_type bytes = 0, map_handle::extent_type offset = 0, section_handle::flag _flag = section_handle::flag::readwrite) noexcept
+{
+ return map_handle::map(std::forward<decltype(section)>(section), std::forward<decltype(bytes)>(bytes), std::forward<decltype(offset)>(offset), std::forward<decltype(_flag)>(_flag));
+}
+//! The size of the memory map. This is the accessible size, NOT the reservation size.
+inline map_handle::size_type length(const map_handle &self) noexcept
+{
+ return self.length();
+}
+/*! Resize the reservation of the memory map without changing the address (unless the map
+was zero sized, in which case a new address will be chosen).
+
+If shrinking, address space is released on POSIX, and on Windows if the new size is zero.
+If the new size is zero, the address is set to null to prevent surprises.
+Windows does not support modifying existing mapped regions, so if the new size is not
+zero, the call will probably fail. Windows should let you truncate a previous extension
+however, if it is exact.
+
+If expanding, an attempt is made to map in new reservation immediately after the current address
+reservation, thus extending the reservation. If anything else is mapped in after
+the current reservation, the function fails.
+
+\note On all supported platforms apart from OS X, proprietary flags exist to avoid
+performing a map if a map extension cannot be immediately placed after the current map. On OS X,
+we hint where we'd like the new map to go, but if something is already there OS X will
+place the map elsewhere. In this situation, we delete the new map and return failure,
+which is inefficient, but there is nothing else we can do.
+
+\return The bytes actually reserved.
+\param self The object whose member function to call.
+\param newsize The bytes to truncate the map reservation to. Rounded up to the nearest page size (POSIX) or 64Kb on Windows.
+\param permit_relocation Permit the address to change (some OSs provide a syscall for resizing
+a memory map).
+\errors Any of the values POSIX `mremap()`, `mmap(addr)` or `VirtualAlloc(addr)` can return.
+*/
+inline result<map_handle::size_type> truncate(map_handle &self, map_handle::size_type newsize, bool permit_relocation = false) noexcept
+{
+ return self.truncate(std::forward<decltype(newsize)>(newsize), std::forward<decltype(permit_relocation)>(permit_relocation));
+}
+/*! \brief Read data from the mapped view.
+
+\note Because this implementation never copies memory, you can pass in buffers with a null address.
+
+\return The buffers read, which will never be the buffers input because they will point into the mapped view.
+The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred.
+\param self The object whose member function to call.
+\param reqs A scatter-gather and offset request.
+\param d Ignored.
+\errors None, though the various signals and structured exception throws common to using memory maps may occur.
+\mallocs None.
+*/
+inline map_handle::io_result<map_handle::buffers_type> read(map_handle &self, map_handle::io_request<map_handle::buffers_type> reqs, deadline d = deadline()) noexcept
+{
+ return self.read(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
+}
+/*! \brief Write data to the mapped view.
+
+\return The buffers written, which will never be the buffers input because they will point at where the data was copied into the mapped view.
+The size of each scatter-gather buffer is updated with the number of bytes of that buffer transferred.
+\param self The object whose member function to call.
+\param reqs A scatter-gather and offset request.
+\param d Ignored.
+\errors None, though the various signals and structured exception throws common to using memory maps may occur.
+\mallocs None.
+*/
+inline map_handle::io_result<map_handle::const_buffers_type> write(map_handle &self, map_handle::io_request<map_handle::const_buffers_type> reqs, deadline d = deadline()) noexcept
+{
+ return self.write(std::forward<decltype(reqs)>(reqs), std::forward<decltype(d)>(d));
+}
+// END make_free_functions.py
+
+AFIO_V2_NAMESPACE_END
+
+#if AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define AFIO_INCLUDED_BY_HEADER 1
+#ifdef _WIN32
+#include "detail/impl/windows/map_handle.ipp"
+#else
+#include "detail/impl/posix/map_handle.ipp"
+#endif
+#undef AFIO_INCLUDED_BY_HEADER
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif