Commit a4d4e086c5

Andrew Kelley <andrew@ziglang.org>
2025-02-04 05:40:57
introduce std.posix.mremap and use it
in std.heap.page_allocator
1 parent a0b2a18
Changed files (3)
lib/std/heap/PageAllocator.zig
@@ -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) {
lib/std/os/linux.zig
@@ -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;
lib/std/posix.zig
@@ -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,