1
1
#![ deny( unsafe_op_in_unsafe_fn) ]
2
2
3
3
use crate :: alloc:: { GlobalAlloc , Layout , System } ;
4
+ use crate :: ffi:: c_void;
4
5
use crate :: ptr;
6
+ use crate :: sync:: atomic:: { AtomicPtr , Ordering } ;
5
7
use crate :: sys:: c;
6
8
use crate :: sys_common:: alloc:: { realloc_fallback, MIN_ALIGN } ;
7
9
@@ -17,6 +19,9 @@ const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
17
19
extern "system" {
18
20
// Get a handle to the default heap of the current process, or null if the operation fails.
19
21
//
22
+ // SAFETY: Successful calls to this function within the same process are assumed to
23
+ // always return the same handle, which remains valid for the entire lifetime of the process.
24
+ //
20
25
// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
21
26
fn GetProcessHeap ( ) -> c:: HANDLE ;
22
27
@@ -66,29 +71,59 @@ extern "system" {
66
71
// Returns a nonzero value if the operation is successful, and zero if the operation fails.
67
72
//
68
73
// SAFETY:
74
+ // - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
69
75
// - `dwFlags` must be set to zero.
70
76
// - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`,
71
77
// that has not already been freed.
72
78
// If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`,
73
79
// must not be dereferenced ever again.
74
80
//
75
- // Note that both `hHeap` is allowed to be any value, and `lpMem` is allowed to be null,
76
- // both of which will not cause the operation to fail.
81
+ // Note that `lpMem` is allowed to be null, which will not cause the operation to fail.
77
82
//
78
83
// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
79
84
fn HeapFree ( hHeap : c:: HANDLE , dwFlags : c:: DWORD , lpMem : c:: LPVOID ) -> c:: BOOL ;
80
85
}
81
86
87
+ // Cached handle to the default heap of the current process.
88
+ // Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed.
89
+ static HEAP : AtomicPtr < c_void > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
90
+
91
+ // Get a handle to the default heap of the current process, or null if the operation fails.
92
+ // SAFETY: If this operation is successful, `HEAP` will be successfully initialized and contain
93
+ // a non-null handle returned by `GetProcessHeap`.
94
+ #[ inline]
95
+ unsafe fn init_or_get_process_heap ( ) -> c:: HANDLE {
96
+ let heap = HEAP . load ( Ordering :: Relaxed ) ;
97
+ if heap. is_null ( ) {
98
+ // `HEAP` has not yet been successfully initialized
99
+ let heap = unsafe { GetProcessHeap ( ) } ;
100
+ if !heap. is_null ( ) {
101
+ // SAFETY: No locking is needed because within the same process,
102
+ // successful calls to `GetProcessHeap` will always return the same value, even on different threads.
103
+ HEAP . store ( heap, Ordering :: Relaxed ) ;
104
+
105
+ // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap`
106
+ heap
107
+ } else {
108
+ // Could not get the current process heap.
109
+ ptr:: null_mut ( )
110
+ }
111
+ } else {
112
+ // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap`
113
+ heap
114
+ }
115
+ }
116
+
82
117
// Header containing a pointer to the start of an allocated block.
83
- // SAFETY: size and alignment must be <= `MIN_ALIGN`.
118
+ // SAFETY: Size and alignment must be <= `MIN_ALIGN`.
84
119
#[ repr( C ) ]
85
120
struct Header ( * mut u8 ) ;
86
121
87
- // Allocates a block of optionally zeroed memory for a given `layout`.
88
- // Returns a pointer satisfying the guarantees of `System` about allocated pointers.
122
+ // Allocate a block of optionally zeroed memory for a given `layout`.
123
+ // SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers.
89
124
#[ inline]
90
125
unsafe fn allocate ( layout : Layout , zeroed : bool ) -> * mut u8 {
91
- let heap = unsafe { GetProcessHeap ( ) } ;
126
+ let heap = unsafe { init_or_get_process_heap ( ) } ;
92
127
if heap. is_null ( ) {
93
128
// Allocation has failed, could not get the current process heap.
94
129
return ptr:: null_mut ( ) ;
@@ -147,14 +182,14 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
147
182
unsafe impl GlobalAlloc for System {
148
183
#[ inline]
149
184
unsafe fn alloc ( & self , layout : Layout ) -> * mut u8 {
150
- // SAFETY: pointers returned by `allocate` satisfy the guarantees of `System`
185
+ // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
151
186
let zeroed = false ;
152
187
unsafe { allocate ( layout, zeroed) }
153
188
}
154
189
155
190
#[ inline]
156
191
unsafe fn alloc_zeroed ( & self , layout : Layout ) -> * mut u8 {
157
- // SAFETY: pointers returned by `allocate` satisfy the guarantees of `System`
192
+ // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
158
193
let zeroed = true ;
159
194
unsafe { allocate ( layout, zeroed) }
160
195
}
@@ -173,24 +208,27 @@ unsafe impl GlobalAlloc for System {
173
208
}
174
209
} ;
175
210
211
+ // SAFETY: because `ptr` has been successfully allocated with this allocator,
212
+ // `HEAP` must have been successfully initialized and contain a non-null handle
213
+ // returned by `GetProcessHeap`.
214
+ let heap = HEAP . load ( Ordering :: Relaxed ) ;
215
+
176
216
// SAFETY: `block` is a pointer to the start of an allocated block.
177
217
unsafe {
178
- let err = HeapFree ( GetProcessHeap ( ) , 0 , block as c:: LPVOID ) ;
218
+ let err = HeapFree ( heap , 0 , block as c:: LPVOID ) ;
179
219
debug_assert ! ( err != 0 , "Failed to free heap memory: {}" , c:: GetLastError ( ) ) ;
180
220
}
181
221
}
182
222
183
223
#[ inline]
184
224
unsafe fn realloc ( & self , ptr : * mut u8 , layout : Layout , new_size : usize ) -> * mut u8 {
185
225
if layout. align ( ) <= MIN_ALIGN {
186
- let heap = unsafe { GetProcessHeap ( ) } ;
187
- if heap. is_null ( ) {
188
- // Reallocation has failed, could not get the current process heap.
189
- return ptr:: null_mut ( ) ;
190
- }
226
+ // SAFETY: because `ptr` has been successfully allocated with this allocator,
227
+ // `HEAP` must have been successfully initialized and contain a non-null handle
228
+ // returned by `GetProcessHeap`.
229
+ let heap = HEAP . load ( Ordering :: Relaxed ) ;
191
230
192
- // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`,
193
- // `ptr` is a pointer to the start of an allocated block.
231
+ // SAFETY: `ptr` is a pointer to the start of an allocated block.
194
232
// The returned pointer points to the start of an allocated block.
195
233
unsafe { HeapReAlloc ( heap, 0 , ptr as c:: LPVOID , new_size) as * mut u8 }
196
234
} else {
0 commit comments