@@ -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
}
0 commit comments