Skip to content

Commit 4cce9e3

Browse files
committed
Cache GetProcessHeap
1 parent b01bf0e commit 4cce9e3

File tree

1 file changed

+54
-16
lines changed

1 file changed

+54
-16
lines changed

Diff for: library/std/src/sys/windows/alloc.rs

+54-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#![deny(unsafe_op_in_unsafe_fn)]
22

33
use crate::alloc::{GlobalAlloc, Layout, System};
4+
use crate::ffi::c_void;
45
use crate::ptr;
6+
use crate::sync::atomic::{AtomicPtr, Ordering};
57
use crate::sys::c;
68
use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN};
79

@@ -17,6 +19,9 @@ const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
1719
extern "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)]
85120
struct 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]
90125
unsafe 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 {
147182
unsafe 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

Comments
 (0)