11#![ deny( unsafe_op_in_unsafe_fn) ]
22
33use crate :: alloc:: { GlobalAlloc , Layout , System } ;
4+ use crate :: ffi:: c_void;
45use crate :: ptr;
6+ use crate :: sync:: atomic:: { AtomicPtr , Ordering } ;
57use crate :: sys:: c;
68use crate :: sys_common:: alloc:: { realloc_fallback, MIN_ALIGN } ;
79
@@ -17,6 +19,9 @@ const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
1719extern "system" {
1820 // Get a handle to the default heap of the current process, or null if the operation fails.
1921 //
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+ //
2025 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
2126 fn GetProcessHeap ( ) -> c:: HANDLE ;
2227
@@ -66,29 +71,59 @@ extern "system" {
6671 // Returns a nonzero value if the operation is successful, and zero if the operation fails.
6772 //
6873 // SAFETY:
74+ // - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
6975 // - `dwFlags` must be set to zero.
7076 // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`,
7177 // that has not already been freed.
7278 // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`,
7379 // must not be dereferenced ever again.
7480 //
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.
7782 //
7883 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
7984 fn HeapFree ( hHeap : c:: HANDLE , dwFlags : c:: DWORD , lpMem : c:: LPVOID ) -> c:: BOOL ;
8085}
8186
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+
82117// 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`.
84119#[ repr( C ) ]
85120struct Header ( * mut u8 ) ;
86121
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.
89124#[ inline]
90125unsafe fn allocate ( layout : Layout , zeroed : bool ) -> * mut u8 {
91- let heap = unsafe { GetProcessHeap ( ) } ;
126+ let heap = unsafe { init_or_get_process_heap ( ) } ;
92127 if heap. is_null ( ) {
93128 // Allocation has failed, could not get the current process heap.
94129 return ptr:: null_mut ( ) ;
@@ -147,14 +182,14 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
147182unsafe impl GlobalAlloc for System {
148183 #[ inline]
149184 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`
151186 let zeroed = false ;
152187 unsafe { allocate ( layout, zeroed) }
153188 }
154189
155190 #[ inline]
156191 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`
158193 let zeroed = true ;
159194 unsafe { allocate ( layout, zeroed) }
160195 }
@@ -173,24 +208,27 @@ unsafe impl GlobalAlloc for System {
173208 }
174209 } ;
175210
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+
176216 // SAFETY: `block` is a pointer to the start of an allocated block.
177217 unsafe {
178- let err = HeapFree ( GetProcessHeap ( ) , 0 , block as c:: LPVOID ) ;
218+ let err = HeapFree ( heap , 0 , block as c:: LPVOID ) ;
179219 debug_assert ! ( err != 0 , "Failed to free heap memory: {}" , c:: GetLastError ( ) ) ;
180220 }
181221 }
182222
183223 #[ inline]
184224 unsafe fn realloc ( & self , ptr : * mut u8 , layout : Layout , new_size : usize ) -> * mut u8 {
185225 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 ) ;
191230
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.
194232 // The returned pointer points to the start of an allocated block.
195233 unsafe { HeapReAlloc ( heap, 0 , ptr as c:: LPVOID , new_size) as * mut u8 }
196234 } else {
0 commit comments