| ... | ... | @@ -6,9 +6,14 @@ const maxInt = std.math.maxInt; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | 7 | const native_os = builtin.os.tag; |
| 8 | 8 | const windows = std.os.windows; |
| 9 | const ntdll = windows.ntdll; |
| 9 | 10 | const posix = std.posix; |
| 10 | 11 | const page_size_min = std.heap.page_size_min; |
| 11 | 12 | |
| 13 | const SUCCESS = @import("../os/windows/ntstatus.zig").NTSTATUS.SUCCESS; |
| 14 | const MEM_RESERVE_PLACEHOLDER = windows.MEM_RESERVE_PLACEHOLDER; |
| 15 | const MEM_PRESERVE_PLACEHOLDER = windows.MEM_PRESERVE_PLACEHOLDER; |
| 16 | |
| 12 | 17 | pub const vtable: Allocator.VTable = .{ |
| 13 | 18 | .alloc = alloc, |
| 14 | 19 | .resize = resize, |
| ... | ... | @@ -22,51 +27,62 @@ pub fn map(n: usize, alignment: mem.Alignment) ?[*]u8 { |
| 22 | 27 | const alignment_bytes = alignment.toByteUnits(); |
| 23 | 28 | |
| 24 | 29 | if (native_os == .windows) { |
| 25 | | // According to official documentation, VirtualAlloc aligns to page |
| 26 | | // boundary, however, empirically it reserves pages on a 64K boundary. |
| 27 | | // Since it is very likely the requested alignment will be honored, |
| 28 | | // this logic first tries a call with exactly the size requested, |
| 29 | | // before falling back to the loop below. |
| 30 | | // https://devblogs.microsoft.com/oldnewthing/?p=42223 |
| 31 | | const addr = windows.VirtualAlloc( |
| 32 | | null, |
| 33 | | // VirtualAlloc will round the length to a multiple of page size. |
| 34 | | // "If the lpAddress parameter is NULL, this value is rounded up to |
| 35 | | // the next page boundary". |
| 36 | | n, |
| 37 | | windows.MEM_COMMIT | windows.MEM_RESERVE, |
| 38 | | windows.PAGE_READWRITE, |
| 39 | | ) catch return null; |
| 40 | | |
| 41 | | if (mem.isAligned(@intFromPtr(addr), alignment_bytes)) |
| 42 | | return @ptrCast(addr); |
| 43 | | |
| 44 | | // Fallback: reserve a range of memory large enough to find a |
| 45 | | // sufficiently aligned address, then free the entire range and |
| 46 | | // immediately allocate the desired subset. Another thread may have won |
| 47 | | // the race to map the target range, in which case a retry is needed. |
| 48 | | windows.VirtualFree(addr, 0, windows.MEM_RELEASE); |
| 30 | var base_addr: ?*anyopaque = null; |
| 31 | var size: windows.SIZE_T = n; |
| 32 | |
| 33 | var status = ntdll.NtAllocateVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), 0, &size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_READWRITE); |
| 34 | |
| 35 | if (status == SUCCESS and mem.isAligned(@intFromPtr(base_addr), alignment_bytes)) { |
| 36 | return @ptrCast(base_addr); |
| 37 | } |
| 38 | |
| 39 | if (status == SUCCESS) { |
| 40 | var region_size: windows.SIZE_T = 0; |
| 41 | _ = ntdll.NtFreeVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), &region_size, windows.MEM_RELEASE); |
| 42 | } |
| 49 | 43 | |
| 50 | 44 | const overalloc_len = n + alignment_bytes - page_size; |
| 51 | 45 | const aligned_len = mem.alignForward(usize, n, page_size); |
| 52 | 46 | |
| 53 | | while (true) { |
| 54 | | const reserved_addr = windows.VirtualAlloc( |
| 55 | | null, |
| 56 | | overalloc_len, |
| 57 | | windows.MEM_RESERVE, |
| 58 | | windows.PAGE_NOACCESS, |
| 59 | | ) catch return null; |
| 60 | | const aligned_addr = mem.alignForward(usize, @intFromPtr(reserved_addr), alignment_bytes); |
| 61 | | windows.VirtualFree(reserved_addr, 0, windows.MEM_RELEASE); |
| 62 | | const ptr = windows.VirtualAlloc( |
| 63 | | @ptrFromInt(aligned_addr), |
| 64 | | aligned_len, |
| 65 | | windows.MEM_COMMIT | windows.MEM_RESERVE, |
| 66 | | windows.PAGE_READWRITE, |
| 67 | | ) catch continue; |
| 68 | | return @ptrCast(ptr); |
| 47 | base_addr = null; |
| 48 | size = overalloc_len; |
| 49 | |
| 50 | status = ntdll.NtAllocateVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), 0, &size, windows.MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, windows.PAGE_NOACCESS); |
| 51 | |
| 52 | if (status != SUCCESS) return null; |
| 53 | |
| 54 | const placeholder_addr = @intFromPtr(base_addr); |
| 55 | const aligned_addr = mem.alignForward(usize, placeholder_addr, alignment_bytes); |
| 56 | const prefix_size = aligned_addr - placeholder_addr; |
| 57 | |
| 58 | if (prefix_size > 0) { |
| 59 | var prefix_base = base_addr; |
| 60 | var prefix_size_param: windows.SIZE_T = prefix_size; |
| 61 | _ = ntdll.NtFreeVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&prefix_base), &prefix_size_param, windows.MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); |
| 69 | 62 | } |
| 63 | |
| 64 | const suffix_start = aligned_addr + aligned_len; |
| 65 | const suffix_size = (placeholder_addr + overalloc_len) - suffix_start; |
| 66 | if (suffix_size > 0) { |
| 67 | var suffix_base = @as(?*anyopaque, @ptrFromInt(suffix_start)); |
| 68 | var suffix_size_param: windows.SIZE_T = suffix_size; |
| 69 | _ = ntdll.NtFreeVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&suffix_base), &suffix_size_param, windows.MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER); |
| 70 | } |
| 71 | |
| 72 | base_addr = @ptrFromInt(aligned_addr); |
| 73 | size = aligned_len; |
| 74 | |
| 75 | status = ntdll.NtAllocateVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), 0, &size, windows.MEM_COMMIT | MEM_PRESERVE_PLACEHOLDER, windows.PAGE_READWRITE); |
| 76 | |
| 77 | if (status == SUCCESS) { |
| 78 | return @ptrCast(base_addr); |
| 79 | } |
| 80 | |
| 81 | base_addr = @as(?*anyopaque, @ptrFromInt(aligned_addr)); |
| 82 | size = aligned_len; |
| 83 | _ = ntdll.NtFreeVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), &size, windows.MEM_RELEASE); |
| 84 | |
| 85 | return null; |
| 70 | 86 | } |
| 71 | 87 | |
| 72 | 88 | const aligned_len = mem.alignForward(usize, n, page_size); |
| ... | ... | @@ -104,26 +120,14 @@ fn alloc(context: *anyopaque, n: usize, alignment: mem.Alignment, ra: usize) ?[* |
| 104 | 120 | return map(n, alignment); |
| 105 | 121 | } |
| 106 | 122 | |
| 107 | | fn resize( |
| 108 | | context: *anyopaque, |
| 109 | | memory: []u8, |
| 110 | | alignment: mem.Alignment, |
| 111 | | new_len: usize, |
| 112 | | return_address: usize, |
| 113 | | ) bool { |
| 123 | fn resize(context: *anyopaque, memory: []u8, alignment: mem.Alignment, new_len: usize, return_address: usize) bool { |
| 114 | 124 | _ = context; |
| 115 | 125 | _ = alignment; |
| 116 | 126 | _ = return_address; |
| 117 | 127 | return realloc(memory, new_len, false) != null; |
| 118 | 128 | } |
| 119 | 129 | |
| 120 | | fn remap( |
| 121 | | context: *anyopaque, |
| 122 | | memory: []u8, |
| 123 | | alignment: mem.Alignment, |
| 124 | | new_len: usize, |
| 125 | | return_address: usize, |
| 126 | | ) ?[*]u8 { |
| 130 | fn remap(context: *anyopaque, memory: []u8, alignment: mem.Alignment, new_len: usize, return_address: usize) ?[*]u8 { |
| 127 | 131 | _ = context; |
| 128 | 132 | _ = alignment; |
| 129 | 133 | _ = return_address; |
| ... | ... | @@ -139,7 +143,9 @@ fn free(context: *anyopaque, memory: []u8, alignment: mem.Alignment, return_addr |
| 139 | 143 | |
| 140 | 144 | pub fn unmap(memory: []align(page_size_min) u8) void { |
| 141 | 145 | if (native_os == .windows) { |
| 142 | | windows.VirtualFree(memory.ptr, 0, windows.MEM_RELEASE); |
| 146 | var base_addr: ?*anyopaque = memory.ptr; |
| 147 | var region_size: windows.SIZE_T = 0; |
| 148 | _ = ntdll.NtFreeVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&base_addr), &region_size, windows.MEM_RELEASE); |
| 143 | 149 | } else { |
| 144 | 150 | const page_aligned_len = mem.alignForward(usize, memory.len, std.heap.pageSize()); |
| 145 | 151 | posix.munmap(memory.ptr[0..page_aligned_len]); |
| ... | ... | @@ -157,13 +163,10 @@ pub fn realloc(uncasted_memory: []u8, new_len: usize, may_move: bool) ?[*]u8 { |
| 157 | 163 | const old_addr_end = base_addr + memory.len; |
| 158 | 164 | const new_addr_end = mem.alignForward(usize, base_addr + new_len, page_size); |
| 159 | 165 | if (old_addr_end > new_addr_end) { |
| 160 | | // For shrinking that is not releasing, we will only decommit |
| 161 | | // the pages not needed anymore. |
| 162 | | windows.VirtualFree( |
| 163 | | @ptrFromInt(new_addr_end), |
| 164 | | old_addr_end - new_addr_end, |
| 165 | | windows.MEM_DECOMMIT, |
| 166 | | ); |
| 166 | var decommit_addr: ?*anyopaque = @ptrFromInt(new_addr_end); |
| 167 | var decommit_size: windows.SIZE_T = old_addr_end - new_addr_end; |
| 168 | |
| 169 | _ = ntdll.NtAllocateVirtualMemory(windows.GetCurrentProcess(), @ptrCast(&decommit_addr), 0, &decommit_size, windows.MEM_RESET, windows.PAGE_NOACCESS); |
| 167 | 170 | } |
| 168 | 171 | return memory.ptr; |
| 169 | 172 | } |