Skip to content

Commit

Permalink
Windows:
Browse files Browse the repository at this point in the history
 - 'fixed'/emulated support for resizeable pagefile backed mappings
 - minor shared mem cleanup/update
 - optimized the mem_info() function.
  • Loading branch information
psiha committed Sep 28, 2024
1 parent 593039c commit dc410d8
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 50 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
51 changes: 44 additions & 7 deletions include/psi/vm/detail/nt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ namespace psi::vm::nt
{
//------------------------------------------------------------------------------

// TODO move to https://github.com/winsiderss/phnt

#ifndef STATUS_SUCCESS
using NTSTATUS = LONG;
NTSTATUS constexpr STATUS_SUCCESS{ 0 };
#endif // STATUS_SUCCESS
#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<HANDLE>( std::intptr_t{ -1 } ) };

Expand All @@ -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_t>( "NtCreateSection" ) };

enum SECTION_INFORMATION_CLASS { SectionBasicInformation, SectionImageInformation };
Expand All @@ -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_t>( "NtQuerySection" ) };

using NtExtendSection_t = NTSTATUS (NTAPI*)
(
IN HANDLE SectionHandle,
IN PLARGE_INTEGER NewSectionSize
);
) noexcept;
inline auto const NtExtendSection{ detail::get_nt_proc<NtExtendSection_t>( "NtExtendSection" ) };

enum SECTION_INHERIT
Expand All @@ -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_t>( "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_t>( "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_t>( "NtAllocateVirtualMemory" ) };
inline auto const NtFreeVirtualMemory { detail::get_nt_proc<NtFreeVirtualMemory_t >( "NtFreeVirtualMemory" ) };
Expand Down
14 changes: 2 additions & 12 deletions include/psi/vm/mappable_objects/shared_memory/win32/mem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,7 @@ class native_named_memory //<lifetime_policy::scoped, resizing_policy::fixed>
{
public:
static
fallible_result<native_named_memory>
BOOST_CC_REG create
fallible_result<native_named_memory> create
(
char const * const name,
std::size_t const size,
Expand All @@ -350,16 +349,7 @@ class native_named_memory //<lifetime_policy::scoped, resizing_policy::fixed>
return detail::create_mapping_impl::do_map( file_handle::reference{ file_handle::traits::invalid_value }, flags, size, name );
}

fallible_result<std::size_t> 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
Expand Down
12 changes: 5 additions & 7 deletions src/allocation/allocation.win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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 )
Expand Down
9 changes: 9 additions & 0 deletions src/mappable_objects/file/file.win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ namespace detail
HANDLE handle{ handle_traits::invalid_value };
LARGE_INTEGER maximum_size{ .QuadPart = static_cast<LONGLONG>( 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<std::uint32_t>::max();
}

auto const nt_result
{
nt::NtCreateSection // TODO use it for named sections also
Expand Down
2 changes: 1 addition & 1 deletion src/mapped_view/mapped_view.win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
43 changes: 26 additions & 17 deletions src/mapping/mapping.win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -43,24 +43,33 @@ namespace detail::create_mapping_impl { HANDLE map_file( file_handle::reference
err::fallible_result<void, nt::error> set_size( mapping & the_mapping, std::uint64_t const new_size ) noexcept
{
using namespace nt;
LARGE_INTEGER ntsz{ .QuadPart = static_cast<LONGLONG>( new_size ) };
auto const result{ NtExtendSection( the_mapping.get(), &ntsz ) };
if ( !NT_SUCCESS( result ) ) [[ unlikely ]]
return result;

BOOST_ASSERT( ntsz.QuadPart >= static_cast<LONGLONG>( new_size ) );
if ( ntsz.QuadPart > static_cast<LONGLONG>( 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<LONGLONG>( 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<LONGLONG>( new_size ) };
auto const result{ NtExtendSection( the_mapping.get(), &ntsz ) };
if ( !NT_SUCCESS( result ) ) [[ unlikely ]]
return result;

BOOST_ASSERT( ntsz.QuadPart >= static_cast<LONGLONG>( new_size ) );
if ( ntsz.QuadPart > static_cast<LONGLONG>( new_size ) )
{
// NtExtendSection does not support downsizing - use it also as a size getter (avoid get_size call)
BOOST_ASSERT( ntsz.QuadPart == static_cast<LONGLONG>( 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;
}
Expand Down

0 comments on commit dc410d8

Please sign in to comment.