Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmake/configuration.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ option(FOONATHAN_MEMORY_EXTERN_TEMPLATE
"whether or not common template instantiations are already provided by the library" ON)
set(FOONATHAN_MEMORY_TEMPORARY_STACK_MODE 2 CACHE STRING
"set to 0 to disable the per-thread stack completely, to 1 to disable the nitfy counter and to 2 to enable everything")
set(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE OFF CACHE BOOL
"enable to provide the deprecated variable foonathan::memory::virtual_memory_page_size")
2 changes: 2 additions & 0 deletions doc/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Mode `2` has a slight runtime overhead.

* `FOONATHAN_MEMORY_DEBUG_*`: Specifies debugging options such as pointer check in `deallocate()` or filling newly allocated memory with values. They are set automatically for certain build types and cannot be overriden: All of them are enabled in `Debug` builds, the faster ones in `RelWithDebInfo` and none in `Release`. See [debugging](md_doc_debug_error.html#debugging) for a detailed description.

* `FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE`: The variable `virtual_memory_page_size` was deprecated in 2022, with release 0.7-2. It will henceforth be removed, unless `FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE` is defined.

A list of all options with description is generated by calling `cmake -LH`.

## Variables and targets
Expand Down
8 changes: 5 additions & 3 deletions include/foonathan/memory/virtual_memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ namespace foonathan
virtual_memory_allocator_leak_checker)
} // namespace detail

#if defined(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE)
/// The page size of the virtual memory.
/// All virtual memory allocations must be multiple of this size.
/// It is usually 4KiB.
/// \ingroup allocator
/// \deprecated use \ref get_virtual_memory_page_size instead.
extern const std::size_t virtual_memory_page_size;
#endif

/// \returns the page size of the virtual memory.
/// All virtual memory allocations must be multiple of this size.
Expand All @@ -48,7 +50,7 @@ namespace foonathan

/// Reserves virtual memory.
/// \effects Reserves the given number of pages.
/// Each page is \ref virtual_memory_page_size big.
/// Each page is \ref get_virtual_memory_page_size big.
/// \returns The address of the first reserved page,
/// or \c nullptr in case of error.
/// \note The memory may not be used, it must first be commited.
Expand Down Expand Up @@ -111,7 +113,7 @@ namespace foonathan
/// \returns The maximum node size by returning the maximum value.
std::size_t max_node_size() const noexcept;

/// \returns The maximum alignment which is the same as the \ref virtual_memory_page_size.
/// \returns The maximum alignment which is the same as the \ref get_virtual_memory_page_size.
std::size_t max_alignment() const noexcept;
};

Expand All @@ -131,7 +133,7 @@ namespace foonathan
public:
/// \effects Creates it giving it the block size and the total number of blocks it can allocate.
/// It reserves enough virtual memory for <tt>block_size * no_blocks</tt>.
/// \requires \c block_size must be non-zero and a multiple of the \ref virtual_memory_page_size.
/// \requires \c block_size must be non-zero and a multiple of the \ref get_virtual_memory_page_size.
/// \c no_blocks must be bigger than \c 1.
/// \throws \ref out_of_memory if it cannot reserve the virtual memory.
explicit virtual_block_allocator(std::size_t block_size, std::size_t no_blocks);
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ target_compile_definitions(foonathan_memory PUBLIC
FOONATHAN_MEMORY_VERSION_MAJOR=$CACHE{FOONATHAN_MEMORY_VERSION_MAJOR}
FOONATHAN_MEMORY_VERSION_MINOR=$CACHE{FOONATHAN_MEMORY_VERSION_MINOR}
FOONATHAN_MEMORY_VERSION_PATCH=$CACHE{FOONATHAN_MEMORY_VERSION_PATCH})
if(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE)
target_compile_definitions(foonathan_memory PUBLIC
FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE=1)
endif()

if(NOT MSVC)
target_compile_features(foonathan_memory PUBLIC cxx_constexpr)
endif()
Expand Down
74 changes: 45 additions & 29 deletions src/virtual_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,25 @@ namespace
}
} // namespace

const std::size_t foonathan::memory::virtual_memory_page_size = get_page_size();
std::size_t foonathan::memory::get_virtual_memory_page_size() noexcept
{
static const std::size_t page_size = get_page_size();
return page_size;
}

#if defined(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE)
const std::size_t foonathan::memory::virtual_memory_page_size =
foonathan::memory::get_virtual_memory_page_size();
#endif

void* foonathan::memory::virtual_memory_reserve(std::size_t no_pages) noexcept
{
auto pages =
#if (_MSC_VER <= 1900) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
VirtualAlloc(nullptr, no_pages * virtual_memory_page_size, MEM_RESERVE, PAGE_READWRITE);
VirtualAlloc(nullptr, no_pages * get_virtual_memory_page_size(), MEM_RESERVE,
PAGE_READWRITE);
#else
VirtualAllocFromApp(nullptr, no_pages * virtual_memory_page_size, MEM_RESERVE,
VirtualAllocFromApp(nullptr, no_pages * get_virtual_memory_page_size(), MEM_RESERVE,
PAGE_READWRITE);
#endif
return pages;
Expand All @@ -56,9 +66,9 @@ void* foonathan::memory::virtual_memory_commit(void* memory, std::size_t no_page
{
auto region =
#if (_MSC_VER <= 1900) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
VirtualAlloc(memory, no_pages * virtual_memory_page_size, MEM_COMMIT, PAGE_READWRITE);
VirtualAlloc(memory, no_pages * get_virtual_memory_page_size(), MEM_COMMIT, PAGE_READWRITE);
#else
VirtualAllocFromApp(memory, no_pages * virtual_memory_page_size, MEM_COMMIT,
VirtualAllocFromApp(memory, no_pages * get_virtual_memory_page_size(), MEM_COMMIT,
PAGE_READWRITE);
#endif
if (!region)
Expand All @@ -69,7 +79,7 @@ void* foonathan::memory::virtual_memory_commit(void* memory, std::size_t no_page

void foonathan::memory::virtual_memory_decommit(void* memory, std::size_t no_pages) noexcept
{
auto result = VirtualFree(memory, no_pages * virtual_memory_page_size, MEM_DECOMMIT);
auto result = VirtualFree(memory, no_pages * get_virtual_memory_page_size(), MEM_DECOMMIT);
FOONATHAN_MEMORY_ASSERT_MSG(result, "cannot decommit memory");
(void)result;
}
Expand All @@ -78,13 +88,21 @@ void foonathan::memory::virtual_memory_decommit(void* memory, std::size_t no_pag
#include <sys/mman.h>
#include <unistd.h>

std::size_t foonathan::memory::get_virtual_memory_page_size() noexcept
{
#if defined(PAGESIZE)
const std::size_t foonathan::memory::virtual_memory_page_size = PAGESIZE;
return PAGESIZE;
#elif defined(PAGE_SIZE)
const std::size_t foonathan::memory::virtual_memory_page_size = PAGE_SIZE;
return PAGE_SIZE;
#else
static const std::size_t page_size = static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
return page_size;
#endif
}

#if defined(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE)
const std::size_t foonathan::memory::virtual_memory_page_size =
static_cast<std::size_t>(sysconf(_SC_PAGESIZE));
foonathan::memory::get_virtual_memory_page_size();
#endif

#ifndef MAP_ANONYMOUS
Expand All @@ -93,21 +111,21 @@ const std::size_t foonathan::memory::virtual_memory_page_size =

void* foonathan::memory::virtual_memory_reserve(std::size_t no_pages) noexcept
{
auto pages = mmap(nullptr, no_pages * virtual_memory_page_size, PROT_NONE,
auto pages = mmap(nullptr, no_pages * get_virtual_memory_page_size(), PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return pages == MAP_FAILED ? nullptr : pages;
}

void foonathan::memory::virtual_memory_release(void* pages, std::size_t no_pages) noexcept
{
auto result = munmap(pages, no_pages * virtual_memory_page_size);
auto result = munmap(pages, no_pages * get_virtual_memory_page_size());
FOONATHAN_MEMORY_ASSERT_MSG(result == 0, "cannot release pages");
(void)result;
}

void* foonathan::memory::virtual_memory_commit(void* memory, std::size_t no_pages) noexcept
{
auto size = no_pages * virtual_memory_page_size;
auto size = no_pages * get_virtual_memory_page_size();
auto result = mprotect(memory, size, PROT_WRITE | PROT_READ);
if (result != 0)
return nullptr;
Expand All @@ -124,7 +142,7 @@ void* foonathan::memory::virtual_memory_commit(void* memory, std::size_t no_page

void foonathan::memory::virtual_memory_decommit(void* memory, std::size_t no_pages) noexcept
{
auto size = no_pages * virtual_memory_page_size;
auto size = no_pages * get_virtual_memory_page_size();
// advise that the memory won't be needed anymore
#if defined(MADV_FREE)
madvise(memory, size, MADV_FREE);
Expand All @@ -142,17 +160,13 @@ void foonathan::memory::virtual_memory_decommit(void* memory, std::size_t no_pag
#warning "virtual memory functions not available on your platform, define your own"
#endif

std::size_t foonathan::memory::get_virtual_memory_page_size() noexcept
{
return virtual_memory_page_size;
}

namespace
{
std::size_t calc_no_pages(std::size_t size) noexcept
{
auto div = size / virtual_memory_page_size;
auto rest = size % virtual_memory_page_size;
auto const page_size = get_virtual_memory_page_size();
auto div = size / page_size;
auto rest = size % page_size;

return div + (rest != 0u) + (detail::debug_fence_size ? 2u : 1u);
}
Expand All @@ -165,15 +179,15 @@ void* virtual_memory_allocator::allocate_node(std::size_t size, std::size_t)
if (!pages || !virtual_memory_commit(pages, no_pages))
FOONATHAN_THROW(
out_of_memory({FOONATHAN_MEMORY_LOG_PREFIX "::virtual_memory_allocator", nullptr},
no_pages * virtual_memory_page_size));
no_pages * get_virtual_memory_page_size()));
on_allocate(size);

return detail::debug_fill_new(pages, size, virtual_memory_page_size);
return detail::debug_fill_new(pages, size, get_virtual_memory_page_size());
}

void virtual_memory_allocator::deallocate_node(void* node, std::size_t size, std::size_t) noexcept
{
auto pages = detail::debug_fill_free(node, size, virtual_memory_page_size);
auto pages = detail::debug_fill_free(node, size, get_virtual_memory_page_size());

on_deallocate(size);

Expand All @@ -189,7 +203,7 @@ std::size_t virtual_memory_allocator::max_node_size() const noexcept

std::size_t virtual_memory_allocator::max_alignment() const noexcept
{
return virtual_memory_page_size;
return get_virtual_memory_page_size();
}

#if FOONATHAN_MEMORY_EXTERN_TEMPLATE
Expand All @@ -199,10 +213,11 @@ template class foonathan::memory::allocator_traits<virtual_memory_allocator>;
virtual_block_allocator::virtual_block_allocator(std::size_t block_size, std::size_t no_blocks)
: block_size_(block_size)
{
FOONATHAN_MEMORY_ASSERT(block_size % virtual_memory_page_size == 0u);
auto const page_size = get_virtual_memory_page_size();
FOONATHAN_MEMORY_ASSERT(block_size % page_size == 0u);
FOONATHAN_MEMORY_ASSERT(no_blocks > 0);
auto total_size = block_size_ * no_blocks;
auto no_pages = total_size / virtual_memory_page_size;
auto no_pages = total_size / page_size;

cur_ = static_cast<char*>(virtual_memory_reserve(no_pages));
if (!cur_)
Expand All @@ -212,14 +227,15 @@ virtual_block_allocator::virtual_block_allocator(std::size_t block_size, std::si

virtual_block_allocator::~virtual_block_allocator() noexcept
{
virtual_memory_release(cur_, static_cast<std::size_t>(end_ - cur_) / virtual_memory_page_size);
virtual_memory_release(cur_,
static_cast<std::size_t>(end_ - cur_) / get_virtual_memory_page_size());
}

memory_block virtual_block_allocator::allocate_block()
{
if (std::size_t(end_ - cur_) < block_size_)
FOONATHAN_THROW(out_of_fixed_memory(info(), block_size_));
auto mem = virtual_memory_commit(cur_, block_size_ / virtual_memory_page_size);
auto mem = virtual_memory_commit(cur_, block_size_ / get_virtual_memory_page_size());
if (!mem)
FOONATHAN_THROW(out_of_fixed_memory(info(), block_size_));
cur_ += block_size_;
Expand All @@ -232,7 +248,7 @@ void virtual_block_allocator::deallocate_block(memory_block block) noexcept
{ return static_cast<char*>(block.memory) == cur_ - block_size_; },
info(), block.memory);
cur_ -= block_size_;
virtual_memory_decommit(cur_, block_size_ / virtual_memory_page_size);
virtual_memory_decommit(cur_, block_size_ / get_virtual_memory_page_size());
}

allocator_info virtual_block_allocator::info() noexcept
Expand Down
3 changes: 2 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ set(tests
memory_resource_adapter.cpp
memory_stack.cpp
segregator.cpp
smart_ptr.cpp)
smart_ptr.cpp
virtual_memory.cpp)

add_executable(foonathan_memory_test ${tests})
target_link_libraries(foonathan_memory_test PRIVATE foonathan_memory doctest::doctest)
Expand Down
29 changes: 29 additions & 0 deletions test/virtual_memory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib

// tests all possible default allocator classes

#include "virtual_memory.hpp"

#include <doctest/doctest.h>

namespace
{

using namespace foonathan::memory;

static void* pages = virtual_memory_reserve(10);

TEST_CASE("can use virtual_memory during static initialization")
{
REQUIRE(pages != nullptr);

virtual_memory_release(pages, 10);
pages = nullptr;

#if defined(FOONATHAN_MEMORY_ALLOW_VIRTUAL_MEMORY_PAGE_SIZE)
CHECK(virtual_memory_page_size == get_virtual_memory_page_size());
#endif
}

} // namespace