introduce std.posix.mremap and use it

in std.heap.page_allocator
This commit is contained in:
Andrew Kelley 2025-02-03 20:40:57 -08:00
parent a0b2a18648
commit a4d4e086c5
3 changed files with 59 additions and 4 deletions

View File

@ -140,7 +140,8 @@ fn free(context: *anyopaque, slice: []u8, alignment: mem.Alignment, return_addre
}
}
fn realloc(memory: []u8, new_len: usize, may_move: bool) ?[*]u8 {
fn realloc(uncasted_memory: []u8, new_len: usize, may_move: bool) ?[*]u8 {
const memory: []align(std.heap.page_size_min) u8 = @alignCast(uncasted_memory);
const page_size = std.heap.pageSize();
const new_size_aligned = mem.alignForward(usize, new_len, page_size);
@ -153,7 +154,7 @@ fn realloc(memory: []u8, new_len: usize, may_move: bool) ?[*]u8 {
// For shrinking that is not releasing, we will only decommit
// the pages not needed anymore.
windows.VirtualFree(
@as(*anyopaque, @ptrFromInt(new_addr_end)),
@ptrFromInt(new_addr_end),
old_addr_end - new_addr_end,
windows.MEM_DECOMMIT,
);
@ -171,10 +172,11 @@ fn realloc(memory: []u8, new_len: usize, may_move: bool) ?[*]u8 {
if (new_size_aligned == page_aligned_len)
return memory.ptr;
const mremap_available = false; // native_os == .linux;
const mremap_available = native_os == .linux;
if (mremap_available) {
// TODO: if the next_mmap_addr_hint is within the remapped range, update it
return posix.mremap(memory, new_len, .{ .MAYMOVE = may_move }, null) catch return null;
const new_memory = posix.mremap(memory.ptr, memory.len, new_len, .{ .MAYMOVE = may_move }, null) catch return null;
return new_memory.ptr;
}
if (new_size_aligned < page_aligned_len) {

View File

@ -305,6 +305,13 @@ pub const MAP = switch (native_arch) {
else => @compileError("missing std.os.linux.MAP constants for this architecture"),
};
pub const MREMAP = packed struct(u32) {
MAYMOVE: bool = false,
FIXED: bool = false,
DONTUNMAP: bool = false,
_: u29 = 0,
};
pub const O = switch (native_arch) {
.x86_64 => packed struct(u32) {
ACCMODE: ACCMODE = .RDONLY,
@ -934,6 +941,17 @@ pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize {
return syscall3(.mprotect, @intFromPtr(address), length, protection);
}
pub fn mremap(old_addr: ?[*]const u8, old_len: usize, new_len: usize, flags: MREMAP, new_addr: ?[*]const u8) usize {
return syscall5(
.mremap,
@intFromPtr(old_addr),
old_len,
new_len,
@as(u32, @bitCast(flags)),
@intFromPtr(new_addr),
);
}
pub const MSF = struct {
pub const ASYNC = 1;
pub const INVALIDATE = 2;

View File

@ -83,6 +83,7 @@ pub const MAP = system.MAP;
pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
pub const MFD = system.MFD;
pub const MMAP2_UNIT = system.MMAP2_UNIT;
pub const MREMAP = system.MREMAP;
pub const MSF = system.MSF;
pub const MSG = system.MSG;
pub const NAME_MAX = system.NAME_MAX;
@ -4809,6 +4810,40 @@ pub fn munmap(memory: []align(page_size_min) const u8) void {
}
}
pub const MRemapError = error{
LockedMemoryLimitExceeded,
/// Either a bug in the calling code, or the operating system abused the
/// EINVAL error code.
InvalidSyscallParameters,
OutOfMemory,
} || UnexpectedError;
pub fn mremap(
old_address: ?[*]align(page_size_min) u8,
old_len: usize,
new_len: usize,
flags: system.MREMAP,
new_address: ?[*]align(page_size_min) u8,
) MRemapError![]align(page_size_min) u8 {
const rc = system.mremap(old_address, old_len, new_len, flags, new_address);
const err: E = if (builtin.link_libc) blk: {
if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..new_len];
break :blk @enumFromInt(system._errno().*);
} else blk: {
const err = errno(rc);
if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..new_len];
break :blk err;
};
switch (err) {
.SUCCESS => unreachable,
.AGAIN => return error.LockedMemoryLimitExceeded,
.INVAL => return error.InvalidSyscallParameters,
.NOMEM => return error.OutOfMemory,
.FAULT => unreachable,
else => return unexpectedErrno(err),
}
}
pub const MSyncError = error{
UnmappedMemory,
PermissionDenied,