]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/windows/alloc.rs
Merge commit '05677b6bd6c938ed760835d9b1f6514992654ae3' into sync_cg_clif-2021-08-06
[rust.git] / library / std / src / sys / windows / alloc.rs
1 #![deny(unsafe_op_in_unsafe_fn)]
2
3 use crate::alloc::{GlobalAlloc, Layout, System};
4 use crate::ffi::c_void;
5 use crate::ptr;
6 use crate::sync::atomic::{AtomicPtr, Ordering};
7 use crate::sys::c;
8 use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
9
10 #[cfg(test)]
11 mod tests;
12
13 // Heap memory management on Windows is done by using the system Heap API (heapapi.h)
14 // See https://docs.microsoft.com/windows/win32/api/heapapi/
15
16 // Flag to indicate that the memory returned by `HeapAlloc` should be zeroed.
17 const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;
18
19 extern "system" {
20     // Get a handle to the default heap of the current process, or null if the operation fails.
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     //
25     // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
26     fn GetProcessHeap() -> c::HANDLE;
27
28     // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`.
29     // The allocated memory may be uninitialized, or zeroed if `dwFlags` is
30     // set to `HEAP_ZERO_MEMORY`.
31     //
32     // Returns a pointer to the newly-allocated memory or null if the operation fails.
33     // The returned pointer will be aligned to at least `MIN_ALIGN`.
34     //
35     // SAFETY:
36     //  - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
37     //  - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`.
38     //
39     // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
40     //
41     // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc
42     fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID;
43
44     // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`,
45     // to a block of at least `dwBytes` bytes, either shrinking the block in place,
46     // or allocating at a new location, copying memory, and freeing the original location.
47     //
48     // Returns a pointer to the reallocated memory or null if the operation fails.
49     // The returned pointer will be aligned to at least `MIN_ALIGN`.
50     // If the operation fails the given block will never have been freed.
51     //
52     // SAFETY:
53     //  - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
54     //  - `dwFlags` must be set to zero.
55     //  - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or
56     //     `HeapReAlloc`, that has not already been freed.
57     // If the block was successfully reallocated at a new location, pointers pointing to
58     // the freed memory, such as `lpMem`, must not be dereferenced ever again.
59     //
60     // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
61     //
62     // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc
63     fn HeapReAlloc(
64         hHeap: c::HANDLE,
65         dwFlags: c::DWORD,
66         lpMem: c::LPVOID,
67         dwBytes: c::SIZE_T,
68     ) -> c::LPVOID;
69
70     // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`.
71     // Returns a nonzero value if the operation is successful, and zero if the operation fails.
72     //
73     // SAFETY:
74     //  - `hHeap` must be a non-null handle returned by `GetProcessHeap`.
75     //  - `dwFlags` must be set to zero.
76     //  - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`,
77     //     that has not already been freed.
78     // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`,
79     // must not be dereferenced ever again.
80     //
81     // Note that `lpMem` is allowed to be null, which will not cause the operation to fail.
82     //
83     // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
84     fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL;
85 }
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 // If this operation is successful, `HEAP` will be successfully initialized and contain
93 // a non-null handle returned by `GetProcessHeap`.
94 #[inline]
95 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::Release);
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
117 // Get a non-null handle to the default heap of the current process.
118 // SAFETY: `HEAP` must have been successfully initialized.
119 #[inline]
120 unsafe fn get_process_heap() -> c::HANDLE {
121     HEAP.load(Ordering::Acquire)
122 }
123
124 // Header containing a pointer to the start of an allocated block.
125 // SAFETY: Size and alignment must be <= `MIN_ALIGN`.
126 #[repr(C)]
127 struct Header(*mut u8);
128
129 // Allocate a block of optionally zeroed memory for a given `layout`.
130 // SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers,
131 // or null if the operation fails. If this returns non-null `HEAP` will have been successfully
132 // initialized.
133 #[inline]
134 unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
135     let heap = init_or_get_process_heap();
136     if heap.is_null() {
137         // Allocation has failed, could not get the current process heap.
138         return ptr::null_mut();
139     }
140
141     // Allocated memory will be either zeroed or uninitialized.
142     let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 };
143
144     if layout.align() <= MIN_ALIGN {
145         // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`.
146         // The returned pointer points to the start of an allocated block.
147         unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 }
148     } else {
149         // Allocate extra padding in order to be able to satisfy the alignment.
150         let total = layout.align() + layout.size();
151
152         // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`.
153         let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 };
154         if ptr.is_null() {
155             // Allocation has failed.
156             return ptr::null_mut();
157         }
158
159         // Create a correctly aligned pointer offset from the start of the allocated block,
160         // and write a header before it.
161
162         let offset = layout.align() - (ptr as usize & (layout.align() - 1));
163         // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()` and the size of the allocated
164         // block is `layout.align() + layout.size()`. `aligned` will thus be a correctly aligned
165         // pointer inside the allocated block with at least `layout.size()` bytes after it and at
166         // least `MIN_ALIGN` bytes of padding before it.
167         let aligned = unsafe { ptr.add(offset) };
168         // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned`
169         // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before
170         // it, it is safe to write a header directly before it.
171         unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) };
172
173         // SAFETY: The returned pointer does not point to the to the start of an allocated block,
174         // but there is a header readable directly before it containing the location of the start
175         // of the block.
176         aligned
177     }
178 }
179
180 // All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the
181 // following properties:
182 //
183 // If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN`
184 // the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block.
185 //
186 // If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN`
187 // the pointer will be aligned to the specified alignment and not point to the start of the allocated block.
188 // Instead there will be a header readable directly before the returned pointer, containing the actual
189 // location of the start of the block.
190 #[stable(feature = "alloc_system_type", since = "1.28.0")]
191 unsafe impl GlobalAlloc for System {
192     #[inline]
193     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
194         // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
195         let zeroed = false;
196         unsafe { allocate(layout, zeroed) }
197     }
198
199     #[inline]
200     unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
201         // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System`
202         let zeroed = true;
203         unsafe { allocate(layout, zeroed) }
204     }
205
206     #[inline]
207     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
208         let block = {
209             if layout.align() <= MIN_ALIGN {
210                 ptr
211             } else {
212                 // The location of the start of the block is stored in the padding before `ptr`.
213
214                 // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null
215                 // and have a header readable directly before it.
216                 unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 }
217             }
218         };
219
220         // SAFETY: because `ptr` has been successfully allocated with this allocator,
221         // `HEAP` must have been successfully initialized.
222         let heap = unsafe { get_process_heap() };
223
224         // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`,
225         // `block` is a pointer to the start of an allocated block.
226         unsafe { HeapFree(heap, 0, block as c::LPVOID) };
227     }
228
229     #[inline]
230     unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
231         if layout.align() <= MIN_ALIGN {
232             // SAFETY: because `ptr` has been successfully allocated with this allocator,
233             // `HEAP` must have been successfully initialized.
234             let heap = unsafe { get_process_heap() };
235
236             // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`,
237             // `ptr` is a pointer to the start of an allocated block.
238             // The returned pointer points to the start of an allocated block.
239             unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 }
240         } else {
241             // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will
242             // correctly handle `ptr` and return a pointer satisfying the guarantees of `System`
243             unsafe { realloc_fallback(self, ptr, layout, new_size) }
244         }
245     }
246 }