diff --git a/README.md b/README.md index 1bb794f..3505e75 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ ### vm - portable, lightweight, powerful, near-zero-overhead memory mapping, virtual memory management, containers and utilities. +(TODO: sync with https://github.com/ned14/llfio) + ##### Sponsored by (at one point or another) -https://farseer.com -https://microblink.com +https://farseer.com +https://microblink.com + ##### Some ancient discussions -https://svn.boost.org/trac/boost/ticket/4827 -http://boost.2283326.n4.nabble.com/interprocess-shared-memory-lifetime-td2603982.html -http://lists.boost.org/Archives/boost/2010/10/172227.php -http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2044.html + +https://svn.boost.org/trac/boost/ticket/4827 +http://boost.2283326.n4.nabble.com/interprocess-shared-memory-lifetime-td2603982.html +http://lists.boost.org/Archives/boost/2010/10/172227.php +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2044.html ##### Dependencies * Boost diff --git a/include/psi/vm/detail/nt.hpp b/include/psi/vm/detail/nt.hpp index 59d0645..fc2e465 100644 --- a/include/psi/vm/detail/nt.hpp +++ b/include/psi/vm/detail/nt.hpp @@ -30,6 +30,8 @@ namespace psi::vm::nt { //------------------------------------------------------------------------------ +// TODO move to https://github.com/winsiderss/phnt + #ifndef STATUS_SUCCESS using NTSTATUS = LONG; NTSTATUS constexpr STATUS_SUCCESS{ 0 }; @@ -37,6 +39,9 @@ NTSTATUS constexpr STATUS_SUCCESS{ 0 }; #ifndef STATUS_CONFLICTING_ADDRESSES auto constexpr STATUS_CONFLICTING_ADDRESSES{ NTSTATUS( 0xC0000018 ) }; #endif +#ifndef STATUS_SECTION_NOT_EXTENDED +auto constexpr STATUS_SECTION_NOT_EXTENDED{ NTSTATUS( 0xC0000087 ) }; +#endif inline auto const current_process{ reinterpret_cast( std::intptr_t{ -1 } ) }; @@ -63,7 +68,7 @@ using NtCreateSection_t = NTSTATUS (NTAPI*) IN ULONG PageAttributess, IN ULONG SectionAttributes, IN HANDLE FileHandle OPTIONAL -); +) noexcept; inline auto const NtCreateSection{ detail::get_nt_proc( "NtCreateSection" ) }; enum SECTION_INFORMATION_CLASS { SectionBasicInformation, SectionImageInformation }; @@ -82,14 +87,14 @@ using NtQuerySection_t = NTSTATUS (NTAPI*) OUT PVOID InformationBuffer, IN ULONG InformationBufferSize, OUT PULONG ResultLength OPTIONAL -); +) noexcept; inline auto const NtQuerySection{ detail::get_nt_proc( "NtQuerySection" ) }; using NtExtendSection_t = NTSTATUS (NTAPI*) ( IN HANDLE SectionHandle, IN PLARGE_INTEGER NewSectionSize -); +) noexcept; inline auto const NtExtendSection{ detail::get_nt_proc( "NtExtendSection" ) }; enum SECTION_INHERIT @@ -109,12 +114,44 @@ using NtMapViewOfSection_t = NTSTATUS (NTAPI*) IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType OPTIONAL, IN ULONG Protect -); +) noexcept; inline auto const NtMapViewOfSection{ detail::get_nt_proc( "NtMapViewOfSection" ) }; -using NtAllocateVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN OUT PVOID * BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG allocation_type, ULONG Protect ); -using NtFreeVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN PVOID * BaseAddress, PSIZE_T RegionSize, ULONG FreeType ); -using NtProtectVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN OUT PVOID * BaseAddress, IN OUT PULONG NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection ); +enum MEMORY_INFORMATION_CLASS +{ + MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION + MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION + MemoryMappedFilenameInformation, // q: UNICODE_STRING + MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION + MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA + MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8 + MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION + MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION + MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION + MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3 + MemoryBasicInformationCapped, // 10 + MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1 + MemoryBadInformation, // since WIN11 + MemoryBadInformationAllProcesses, // since 22H1 + MemoryImageExtensionInformation, // MEMORY_IMAGE_EXTENSION_INFORMATION // since 24H2 + MaxMemoryInfoClass +}; + +using NtQueryVirtualMemory_t = NTSTATUS (NTAPI *) +( + HANDLE ProcessHandle, + PVOID BaseAddress, + MEMORY_INFORMATION_CLASS MemoryInformationClass, + PVOID MemoryInformation, + SIZE_T MemoryInformationLength, + PSIZE_T ReturnLength +) noexcept; +inline auto const NtQueryVirtualMemory{ detail::get_nt_proc( "NtQueryVirtualMemory" ) }; + + +using NtAllocateVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN OUT PVOID * BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG allocation_type, ULONG Protect ) noexcept; +using NtFreeVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN PVOID * BaseAddress, PSIZE_T RegionSize, ULONG FreeType ) noexcept; +using NtProtectVirtualMemory_t = NTSTATUS (NTAPI*)( IN HANDLE ProcessHandle, IN OUT PVOID * BaseAddress, IN OUT PULONG NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection ) noexcept; inline auto const NtAllocateVirtualMemory{ detail::get_nt_proc( "NtAllocateVirtualMemory" ) }; inline auto const NtFreeVirtualMemory { detail::get_nt_proc( "NtFreeVirtualMemory" ) }; diff --git a/include/psi/vm/mappable_objects/shared_memory/win32/mem.hpp b/include/psi/vm/mappable_objects/shared_memory/win32/mem.hpp index 88838fb..5e6b8ed 100644 --- a/include/psi/vm/mappable_objects/shared_memory/win32/mem.hpp +++ b/include/psi/vm/mappable_objects/shared_memory/win32/mem.hpp @@ -339,8 +339,7 @@ class native_named_memory // { public: static - fallible_result - BOOST_CC_REG create + fallible_result create ( char const * const name, std::size_t const size, @@ -350,16 +349,7 @@ class native_named_memory // return detail::create_mapping_impl::do_map( file_handle::reference{ file_handle::traits::invalid_value }, flags, size, name ); } - fallible_result size() const noexcept - { - auto const p_view( ::MapViewOfFile( get(), 0, 0, 0, 0 ) ); - if ( BOOST_UNLIKELY( !p_view ) ) return error(); - MEMORY_BASIC_INFORMATION info; - BOOST_VERIFY( ::VirtualQuery( p_view, &info, sizeof( info ) ) == sizeof( info ) ); - BOOST_VERIFY( ::UnmapViewOfFile( p_view ) ); - BOOST_ASSUME( info.RegionSize % page_size == 0 ); - return info.RegionSize; - } + auto size() const noexcept { return get_size( *this ); } private: /// \note Required to enable the emplacement constructors of err diff --git a/src/allocation/allocation.win32.cpp b/src/allocation/allocation.win32.cpp index 7977c4f..de57382 100644 --- a/src/allocation/allocation.win32.cpp +++ b/src/allocation/allocation.win32.cpp @@ -93,11 +93,11 @@ void dealloc( void * & address, std::size_t & size, deallocation_type const type #endif } -MEMORY_BASIC_INFORMATION mem_info( void * const address ) noexcept +WIN32_MEMORY_REGION_INFORMATION mem_info( void * const address ) noexcept { - MEMORY_BASIC_INFORMATION info; - BOOST_VERIFY( ::VirtualQueryEx( nt::current_process, address, &info, sizeof( info ) ) == sizeof( info ) ); - BOOST_ASSUME( info.BaseAddress == address ); + WIN32_MEMORY_REGION_INFORMATION info; + BOOST_VERIFY( nt::NtQueryVirtualMemory( nt::current_process, address, nt::MemoryRegionInformation, &info, sizeof( info ), nullptr ) == nt::STATUS_SUCCESS ); + BOOST_ASSUME( info.AllocationBase == address ); return info; } @@ -119,9 +119,7 @@ bool commit( void * const desired_location, std::size_t const size ) noexcept { auto const info{ mem_info( final_address ) }; BOOST_ASSUME( info.AllocationProtect == PAGE_READWRITE ); - BOOST_ASSUME( info.State == MEM_RESERVE ); - BOOST_ASSUME( info.Protect == 0 ); - BOOST_ASSUME( info.Type == MEM_PRIVATE ); + BOOST_ASSUME( info.Private ); auto region_size{ std::min( static_cast< std::size_t >( info.RegionSize ), size - final_size ) }; auto const partial_result{ alloc( final_address, region_size, allocation_type::commit ) }; if ( partial_result != STATUS_SUCCESS ) diff --git a/src/mappable_objects/file/file.win32.cpp b/src/mappable_objects/file/file.win32.cpp index a59753e..133ea1c 100644 --- a/src/mappable_objects/file/file.win32.cpp +++ b/src/mappable_objects/file/file.win32.cpp @@ -118,6 +118,15 @@ namespace detail HANDLE handle{ handle_traits::invalid_value }; LARGE_INTEGER maximum_size{ .QuadPart = static_cast( size ) }; BOOST_ASSERT_MSG( std::uint64_t( maximum_size.QuadPart ) == size, "Unsupported section size" ); + if ( !file ) + { + // Windows (11 23H2) does not (still) seem to support resizing of pagefile-backed mappings + // so we emulate those by creating a 4GB one and counting on: + // - the NT kernel to be lazy and + // - that amount to be 'enough for everyone'. + maximum_size.QuadPart = std::numeric_limits::max(); + } + auto const nt_result { nt::NtCreateSection // TODO use it for named sections also diff --git a/src/mapped_view/mapped_view.win32.cpp b/src/mapped_view/mapped_view.win32.cpp index 9793a57..1c83636 100644 --- a/src/mapped_view/mapped_view.win32.cpp +++ b/src/mapped_view/mapped_view.win32.cpp @@ -100,7 +100,7 @@ map } // defined in allocation.win32.cpp -MEMORY_BASIC_INFORMATION mem_info( void * const ) noexcept; +WIN32_MEMORY_REGION_INFORMATION mem_info( void * const ) noexcept; namespace { diff --git a/src/mapping/mapping.win32.cpp b/src/mapping/mapping.win32.cpp index d4efb54..c3bc16c 100644 --- a/src/mapping/mapping.win32.cpp +++ b/src/mapping/mapping.win32.cpp @@ -34,7 +34,7 @@ std::uint64_t get_size( mapping::const_handle const mapping_handle ) noexcept using namespace nt; SECTION_BASIC_INFORMATION info; auto const result{ NtQuerySection( mapping_handle.value, SECTION_INFORMATION_CLASS::SectionBasicInformation, &info, sizeof( info ), nullptr ) }; - BOOST_VERIFY( NT_SUCCESS( result ) ); + BOOST_ASSUME( result == STATUS_SUCCESS ); return info.SectionSize.QuadPart; } @@ -43,24 +43,33 @@ namespace detail::create_mapping_impl { HANDLE map_file( file_handle::reference err::fallible_result set_size( mapping & the_mapping, std::uint64_t const new_size ) noexcept { using namespace nt; - LARGE_INTEGER ntsz{ .QuadPart = static_cast( new_size ) }; - auto const result{ NtExtendSection( the_mapping.get(), &ntsz ) }; - if ( !NT_SUCCESS( result ) ) [[ unlikely ]] - return result; - BOOST_ASSERT( ntsz.QuadPart >= static_cast( new_size ) ); - if ( ntsz.QuadPart > static_cast( new_size ) ) + if ( the_mapping.is_file_based() ) { - // NtExtendSection does not support downsizing - use it also as a size getter (avoid get_size call) - BOOST_ASSERT( ntsz.QuadPart == static_cast( get_size( the_mapping ) ) ); - the_mapping.close(); // no strong guarantee :/ - auto const file_reisze_result{ set_size( the_mapping.underlying_file(), new_size )() }; - if ( !file_reisze_result ) - return nt::error/*...mrmlj...*/( file_reisze_result.error().get() ); // TODO fully move to NativeNT API https://cpp.hotexamples.com/examples/-/-/NtSetInformationFile/cpp-ntsetinformationfile-function-examples.html - auto const new_mapping_handle{ detail::create_mapping_impl::map_file( the_mapping.file, the_mapping.create_mapping_flags, new_size ) }; - the_mapping.reset( new_mapping_handle ); - if ( !the_mapping ) - return nt::error/*...mrmlj...*/( err::last_win32_error::get() ); + LARGE_INTEGER ntsz{ .QuadPart = static_cast( new_size ) }; + auto const result{ NtExtendSection( the_mapping.get(), &ntsz ) }; + if ( !NT_SUCCESS( result ) ) [[ unlikely ]] + return result; + + BOOST_ASSERT( ntsz.QuadPart >= static_cast( new_size ) ); + if ( ntsz.QuadPart > static_cast( new_size ) ) + { + // NtExtendSection does not support downsizing - use it also as a size getter (avoid get_size call) + BOOST_ASSERT( ntsz.QuadPart == static_cast( get_size( the_mapping ) ) ); + the_mapping.close(); // no strong guarantee :/ + auto const file_reisze_result{ set_size( the_mapping.underlying_file(), new_size )() }; + if ( !file_reisze_result ) + return nt::error/*...mrmlj...*/( file_reisze_result.error().get() ); // TODO fully move to NativeNT API https://cpp.hotexamples.com/examples/-/-/NtSetInformationFile/cpp-ntsetinformationfile-function-examples.html + auto const new_mapping_handle{ detail::create_mapping_impl::map_file( the_mapping.file, the_mapping.create_mapping_flags, new_size ) }; + the_mapping.reset( new_mapping_handle ); + if ( !the_mapping ) + return nt::error/*...mrmlj...*/( err::last_win32_error::get() ); + } + } + else + { + if ( new_size > get_size( the_mapping ) ) [[ unlikely ]] + return nt::STATUS_SECTION_NOT_EXTENDED; } return err::success; }