Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Windows Support for Lazy Commit of page map #727

Merged
merged 3 commits into from
Jan 9, 2025
Merged
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
86 changes: 85 additions & 1 deletion src/snmalloc/pal/pal_windows.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "../aal/aal.h"
#include "../ds_aal/ds_aal.h"
#include "pal_timer_default.h"

#ifdef _WIN32
Expand Down Expand Up @@ -48,13 +49,76 @@ namespace snmalloc
low_memory_callbacks.notify_all();
}

// A list of reserved ranges, used to handle lazy commit on readonly pages.
// We currently only need one, so haven't implemented a backup if the
// initial 16 is insufficient.
inline static std::array<std::pair<address_t, size_t>, 16> reserved_ranges;

// Lock for the reserved ranges.
inline static FlagWord reserved_ranges_lock{};

// Exception handler for handling lazy commit on readonly pages.
static LONG NTAPI
HandleReadonlyLazyCommit(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
// Check this is an AV
if (
ExceptionInfo->ExceptionRecord->ExceptionCode !=
EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_CONTINUE_SEARCH;

// Check this is a read access
if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] != 0)
return EXCEPTION_CONTINUE_SEARCH;

// Get faulting address from exception info.
snmalloc::address_t faulting_address =
ExceptionInfo->ExceptionRecord->ExceptionInformation[1];

bool found = false;
{
FlagLock lock(reserved_ranges_lock);
// Check if the address is in a reserved range.
for (auto& r : reserved_ranges)
{
if ((faulting_address - r.first) < r.second)
{
found = true;
break;
}
}
}

if (!found)
return EXCEPTION_CONTINUE_SEARCH;

// Commit the page as readonly
auto pagebase = snmalloc::bits::align_down(faulting_address, page_size);
VirtualAlloc((void*)pagebase, page_size, MEM_COMMIT, PAGE_READONLY);

// Resume execution
return EXCEPTION_CONTINUE_EXECUTION;
}

static void initialise_for_singleton(size_t*) noexcept
{
AddVectoredExceptionHandler(1, HandleReadonlyLazyCommit);
}

// Ensure the exception handler is registered.
static void initialise_readonly_av() noexcept
{
static Singleton<size_t, &initialise_for_singleton> init;
init.get();
}

public:
/**
* Bitmap of PalFeatures flags indicating the optional features that this
* PAL supports. This PAL supports low-memory notifications.
*/
static constexpr uint64_t pal_features = LowMemoryNotification | Entropy |
Time
Time | LazyCommit
# if defined(PLATFORM_HAS_VIRTUALALLOC2) && !defined(USE_SYSTEMATIC_TESTING)
| AlignedAllocation
# endif
Expand Down Expand Up @@ -155,6 +219,26 @@ namespace snmalloc
"out of memory: {} ({}) could not be committed", p, size);
}

static void notify_using_readonly(void* p, size_t size) noexcept
{
initialise_readonly_av();

{
FlagLock lock(reserved_ranges_lock);
for (auto& r : reserved_ranges)
{
if (r.first == 0)
{
r.first = (address_t)p;
r.second = size;
return;
}
}
}

error("Implementation error: Too many lazy commit regions!");
}

/// OS specific function for zeroing memory
template<bool page_aligned = false>
static void zero(void* p, size_t size) noexcept
Expand Down
Loading