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:
Diffstat (limited to 'include/llfio/v2.0/map_handle.hpp')
-rw-r--r--include/llfio/v2.0/map_handle.hpp175
1 files changed, 133 insertions, 42 deletions
diff --git a/include/llfio/v2.0/map_handle.hpp b/include/llfio/v2.0/map_handle.hpp
index 69241720..ba6b83ef 100644
--- a/include/llfio/v2.0/map_handle.hpp
+++ b/include/llfio/v2.0/map_handle.hpp
@@ -1,5 +1,5 @@
/* A handle to a source of mapped memory
-(C) 2016-2018 Niall Douglas <http://www.nedproductions.biz/> (14 commits)
+(C) 2016-2021 Niall Douglas <http://www.nedproductions.biz/> (14 commits)
File Created: August 2016
@@ -55,28 +55,32 @@ public:
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.
- write_via_syscall = 1U << 18U, //!< For file backed maps, `map_handle::write()` is implemented as a `write()` syscall to the file descriptor. This causes the map to be mapped read-only.
-
- page_sizes_1 = 1U << 24U, //!< Use `utils::page_sizes()[1]` sized pages, or fail.
- page_sizes_2 = 2U << 24U, //!< Use `utils::page_sizes()[2]` sized pages, or fail.
- page_sizes_3 = 3U << 24U, //!< Use `utils::page_sizes()[3]` sized pages, or fail.
-
- // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
-
- readwrite = (read | write)};
+ 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.
+ write_via_syscall =
+ 1U
+ << 18U, //!< For file backed maps, `map_handle::write()` is implemented as a `write()` syscall to the file descriptor. This causes the map to be mapped read-only.
+
+ page_sizes_1 = 1U << 24U, //!< Use `utils::page_sizes()[1]` sized pages, or fail.
+ page_sizes_2 = 2U << 24U, //!< Use `utils::page_sizes()[2]` sized pages, or fail.
+ page_sizes_3 = 3U << 24U, //!< Use `utils::page_sizes()[3]` sized pages, or fail.
+
+ // NOTE: IF UPDATING THIS UPDATE THE std::ostream PRINTER BELOW!!!
+
+ readwrite = (read | write)};
QUICKCPPLIB_BITFIELD_END(flag);
protected:
@@ -154,7 +158,10 @@ public:
\errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
*/
LLFIO_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)); }
+ 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.
@@ -163,7 +170,9 @@ public:
\errors Any of the values POSIX dup(), open() or NtCreateSection() can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_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;
+ static LLFIO_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; }
@@ -454,7 +463,8 @@ protected:
if(section != nullptr)
{
// Apart from read/write/cow/execute, the section's flags override the map's flags
- _flag |= (section->section_flags() & ~(section_handle::flag::read | section_handle::flag::write | section_handle::flag::cow | section_handle::flag::execute));
+ _flag |=
+ (section->section_flags() & ~(section_handle::flag::read | section_handle::flag::write | section_handle::flag::cow | section_handle::flag::execute));
}
}
@@ -463,7 +473,8 @@ public:
constexpr map_handle() {} // NOLINT
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC ~map_handle() override;
//! Construct an instance managing pages at `addr`, `length`, `pagesize` and `flags`
- explicit map_handle(byte *addr, size_type length, size_type pagesize, section_handle::flag flags, section_handle *section = nullptr, extent_type offset = 0) noexcept
+ explicit map_handle(byte *addr, size_type length, size_type pagesize, section_handle::flag flags, section_handle *section = nullptr,
+ extent_type offset = 0) noexcept
: _section(section)
, _addr(addr)
, _offset(offset)
@@ -526,10 +537,18 @@ public:
protected:
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC size_t _do_max_buffers() const noexcept override { return 0; }
- LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> _do_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(), barrier_kind kind = barrier_kind::nowait_data_only, deadline d = deadline()) noexcept override;
+ LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> _do_barrier(io_request<const_buffers_type> reqs = io_request<const_buffers_type>(),
+ barrier_kind kind = barrier_kind::nowait_data_only,
+ deadline d = deadline()) noexcept override;
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> _do_read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept override;
LLFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> _do_write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override;
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> _new_map(size_type bytes, section_handle::flag _flag) noexcept;
+
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> _recycled_map(size_type bytes, section_handle::flag _flag) noexcept;
+
+ LLFIO_HEADERS_ONLY_MEMFUNC_SPEC bool _recycle_map() noexcept;
+
public:
/*! Map unused memory into view, creating new memory if insufficient unused memory is available
(i.e. add the returned memory to the process' commit charge, unless `flag::nocommit`
@@ -538,7 +557,9 @@ public:
\param bytes How many bytes to map. Typically will be rounded up to a multiple of the page size
(see `page_size()`).
- \param zeroed Set to true if only all bits zeroed memory is wanted.
+ \param zeroed Set to true if only all bits zeroed memory is wanted. If this is true, a syscall
+ is always performed as the kernel probably has zeroed pages ready to go, whereas if false, the
+ request may be satisfied from a local cache instead. The default is false.
\param _flag The permissions with which to map the view.
\note On Microsoft Windows this constructor uses the faster `VirtualAlloc()` which creates less
@@ -547,10 +568,35 @@ public:
the other constructor. This makes available all those very useful VM tricks Windows can do with
section mapped memory which `VirtualAlloc()` memory cannot do.
+ When this kind of map handle is closed, it is added to an internal cache so new map
+ handle creations of this kind with `zeroed = false` are very quick and avoid a syscall. The
+ internal cache may return a map slightly bigger than requested. If you wish to always invoke
+ the syscall, specify `zeroed = true`.
+
+ When maps are added to the internal cache, on all systems except Linux the memory is decommitted
+ first. This reduces commit charge appropriately, thus only virtual address space remains
+ consumed. On Linux, if `memory_accounting()` is `memory_accounting_kind::commit_charge`, we also
+ decommit, however be aware that this can increase the average VMA use count in the Linux kernel, and most
+ Linux kernels are configured with a very low per-process limit of 64k VMAs (this is easy to raise
+ using `sysctl -w vm.max_map_count=262144`). Otherwise on Linux to avoid increasing VMA count
+ we instead mark closed maps as `LazyFree`, which means that their contents can be arbitrarily
+ disposed of by the Linux kernel as needed, but also allows Linux to coalesce VMAs so the very
+ low per-process limit is less likely to be exceeded. If the `LazyFree` syscall is not implemented on this
+ Linux, we do nothing.
+
+ \warning The cache does not self-trim on its own, you MUST call `trim_cache()` to trim allocations of
+ virtual address (these don't count towards process commit charge, but they do consume address space
+ and precious VMAs in the Linux kernel). Only on 32 bit processes where virtual address space is limited, or on
+ Linux where VMAs allocated is considered by the Linux OOM killer, will you need to probably
+ care much about regular cache trimming.
+
\errors Any of the values POSIX `mmap()` or `VirtualAlloc()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<map_handle> map(size_type bytes, bool zeroed = false, section_handle::flag _flag = section_handle::flag::readwrite) noexcept;
+ static inline result<map_handle> map(size_type bytes, bool zeroed = false, section_handle::flag _flag = section_handle::flag::readwrite) noexcept
+ {
+ return (zeroed || (_flag & section_handle::flag::nocommit)) ? _new_map(bytes, _flag) : _recycled_map(bytes, _flag);
+ }
/*! Reserve address space within which individual pages can later be committed. Reserved address
space is NOT added to the process' commit charge.
@@ -566,7 +612,7 @@ public:
\errors Any of the values POSIX `mmap()` or `VirtualAlloc()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static inline result<map_handle> reserve(size_type bytes) noexcept { return map(bytes, false, section_handle::flag::none | section_handle::flag::nocommit); }
+ static inline result<map_handle> reserve(size_type bytes) noexcept { return _new_map(bytes, section_handle::flag::none | section_handle::flag::nocommit); }
/*! Create a memory mapped view of a backing storage, optionally reserving additional address
space for later growth.
@@ -585,7 +631,41 @@ public:
\errors Any of the values POSIX `mmap()` or `NtMapViewOfSection()` can return.
*/
LLFIO_MAKE_FREE_FUNCTION
- static LLFIO_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;
+ static LLFIO_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 kind of memory accounting this system uses
+ enum class memory_accounting_kind
+ {
+ unknown,
+ /*! This system will not permit more than physical RAM and your swap files to be committed.
+ On every OS except for Linux, this is always the case.
+ */
+ commit_charge,
+ /*! This system will permit more memory to be committed than physical RAM and your swap
+ files, and will terminate your process without warning at some unknown point
+ if you write into enough of the pages committed. This is typically the default on Linux,
+ but it can be changed at runtime.
+ */
+ over_commit
+ };
+#ifdef __linux__
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC memory_accounting_kind memory_accounting() noexcept;
+#else
+ static memory_accounting_kind memory_accounting() noexcept { return memory_accounting_kind::commit_charge; }
+#endif
+
+ //! Statistics about the map handle cache
+ struct cache_statistics
+ {
+ size_t items_in_cache{0};
+ size_t bytes_in_cache{0};
+ size_t items_just_trimmed{0};
+ size_t bytes_just_trimmed{0};
+ };
+ /*! Get statistics about the map handle cache, optionally trimming the least recently used maps.
+ */
+ static LLFIO_HEADERS_ONLY_MEMFUNC_SPEC cache_statistics trim_cache(std::chrono::steady_clock::time_point older_than = {}) noexcept;
//! The memory section this handle is using
section_handle *section() const noexcept { return _section; }
@@ -718,7 +798,8 @@ public:
*/
LLFIO_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.
+ //! 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 LLFIO_HEADERS_ONLY_MEMFUNC_SPEC result<span<buffer_type>> prefetch(span<buffer_type> regions) noexcept;
//! \overload
static result<buffer_type> prefetch(buffer_type region) noexcept
@@ -898,7 +979,8 @@ inline void swap(section_handle &self, section_handle &o) noexcept
*/
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));
+ 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.
@@ -919,7 +1001,8 @@ inline result<section_handle> section(file_handle &backing, section_handle::exte
\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
+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));
}
@@ -952,7 +1035,8 @@ inline result<void> close(map_handle &self) noexcept
/*! 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.
-\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.
+\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
@@ -968,14 +1052,18 @@ inline result<map_handle> map(map_handle::size_type bytes, bool zeroed = false,
/*! 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 `page_size()`), 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.
+\param offset The offset into the backing storage to map from. Typically needs to be at least a multiple of the page size (see `page_size()`), 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
+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));
+ 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
@@ -1024,7 +1112,8 @@ The size of each scatter-gather buffer is updated with the number of bytes of th
\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
+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));
}
@@ -1038,7 +1127,8 @@ The size of each scatter-gather buffer is updated with the number of bytes of th
\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
+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));
}
@@ -1094,6 +1184,7 @@ LLFIO_V2_NAMESPACE_END
#include "detail/impl/posix/map_handle.ipp"
#endif
#undef LLFIO_INCLUDED_BY_HEADER
+#include "detail/impl/map_handle.ipp"
#endif
#ifdef _MSC_VER