Skip to content

Commit 33a20e6

Browse files
CoAlloc: Allocator + Global API
1 parent 5243ea5 commit 33a20e6

File tree

4 files changed

+211
-1
lines changed

4 files changed

+211
-1
lines changed

library/core/src/alloc/global.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
use crate::alloc::Layout;
22
use crate::cmp;
33
use crate::ptr;
4+
use crate::alloc::GlobalCoAllocMeta;
5+
6+
#[unstable(feature = "global_co_alloc_meta", issue = "none")]
7+
#[allow(missing_debug_implementations)]
8+
/// Used for parameters and results (to/from `GlobalCoAllocator`'s functions, where applicable).
9+
pub struct RawAndMeta {
10+
pub ptr: *mut u8,
11+
pub meta: GlobalCoAllocMeta,
12+
}
413

514
/// A memory allocator that can be registered as the standard library’s default
615
/// through the `#[global_allocator]` attribute.
@@ -156,6 +165,9 @@ pub unsafe trait GlobalAlloc {
156165
#[stable(feature = "global_alloc", since = "1.28.0")]
157166
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
158167

168+
#[unstable(feature = "global_co_alloc", issue = "none")]
169+
unsafe fn co_alloc(&self, _layout: Layout, mut _result: &mut RawAndMeta) {panic!("TODO")}
170+
159171
/// Deallocate the block of memory at the given `ptr` pointer with the given `layout`.
160172
///
161173
/// # Safety
@@ -171,6 +183,9 @@ pub unsafe trait GlobalAlloc {
171183
#[stable(feature = "global_alloc", since = "1.28.0")]
172184
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
173185

186+
#[unstable(feature = "global_co_alloc", issue = "none")]
187+
unsafe fn co_dealloc(&self, _ptr_and_meta: RawAndMeta, _layout: Layout) {panic!("TODO")}
188+
174189
/// Behaves like `alloc`, but also ensures that the contents
175190
/// are set to zero before being returned.
176191
///
@@ -198,11 +213,23 @@ pub unsafe trait GlobalAlloc {
198213
if !ptr.is_null() {
199214
// SAFETY: as allocation succeeded, the region from `ptr`
200215
// of size `size` is guaranteed to be valid for writes.
201-
unsafe { ptr::write_bytes(ptr, 0, size) };
216+
unsafe { ptr::write_bytes(ptr, 0u8, size) };
202217
}
203218
ptr
204219
}
205220

221+
#[unstable(feature = "global_co_alloc", issue = "none")]
222+
unsafe fn co_alloc_zeroed(&self, layout: Layout, mut result: &mut RawAndMeta) {
223+
let size = layout.size();
224+
// SAFETY: the safety contract for `alloc` must be upheld by the caller.
225+
unsafe { self.co_alloc(layout, &mut result) };
226+
if !result.ptr.is_null() {
227+
// SAFETY: as allocation succeeded, the region from `ptr_and_meta.ptr` of size `size` is
228+
// guaranteed to be valid for writes.
229+
unsafe { ptr::write_bytes(result.ptr, 0u8, size) };
230+
}
231+
}
232+
206233
/// Shrink or grow a block of memory to the given `new_size` in bytes.
207234
/// The block is described by the given `ptr` pointer and `layout`.
208235
///
@@ -275,4 +302,31 @@ pub unsafe trait GlobalAlloc {
275302
}
276303
new_ptr
277304
}
305+
306+
#[unstable(feature = "global_co_alloc", issue = "none")]
307+
unsafe fn co_realloc(
308+
&self,
309+
ptr_and_meta: RawAndMeta,
310+
layout: Layout,
311+
new_size: usize,
312+
mut result: &mut RawAndMeta
313+
) {
314+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
315+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
316+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
317+
// SAFETY: the caller must ensure that `new_layout` is greater than zero.
318+
unsafe { self.co_alloc(new_layout, &mut result) };
319+
if !result.ptr.is_null() {
320+
// SAFETY: the previously allocated block cannot overlap the newly allocated block.
321+
// The safety contract for `dealloc` must be upheld by the caller.
322+
unsafe {
323+
ptr::copy_nonoverlapping(
324+
ptr_and_meta.ptr,
325+
result.ptr,
326+
cmp::min(layout.size(), new_size),
327+
);
328+
self.co_dealloc(ptr_and_meta, layout);
329+
}
330+
}
331+
}
278332
}

library/core/src/alloc/mod.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ use crate::error::Error;
2525
use crate::fmt;
2626
use crate::ptr::{self, NonNull};
2727

28+
// @TODO Make this target-specific
29+
#[unstable(feature = "global_co_alloc_meta", issue = "none")]
30+
#[allow(missing_debug_implementations)]
31+
pub struct GlobalCoAllocMeta {
32+
//pub one: usize,
33+
/*pub two: usize,
34+
pub three: usize,
35+
pub four: usize,*/
36+
}
37+
2838
/// The `AllocError` error indicates an allocation failure
2939
/// that may be due to resource exhaustion or to
3040
/// something wrong when combining the given input arguments with this
@@ -48,6 +58,30 @@ impl fmt::Display for AllocError {
4858
}
4959
}
5060

61+
#[unstable(feature = "global_co_alloc_meta", issue = "none")]
62+
#[allow(missing_debug_implementations)]
63+
pub struct PtrAndMeta {
64+
pub ptr: NonNull<u8>,
65+
pub meta: GlobalCoAllocMeta,
66+
}
67+
68+
#[unstable(feature = "global_co_alloc_meta", issue = "none")]
69+
#[allow(missing_debug_implementations)]
70+
/// Used for results (from `CoAllocator`'s functions, where applicable).
71+
pub struct SliceAndMeta {
72+
pub slice: NonNull<[u8]>,
73+
pub meta: GlobalCoAllocMeta,
74+
}
75+
76+
#[unstable(feature = "global_co_alloc_meta", issue = "none")]
77+
#[allow(missing_debug_implementations)]
78+
pub type SliceAndMetaResult = Result<SliceAndMeta, AllocError>;
79+
80+
#[unstable(feature = "global_co_alloc", issue = "none")]
81+
pub const fn co_alloc_metadata_num_slots<A: Allocator>() -> usize {
82+
if A::IS_CO_ALLOCATOR { 1 } else { 0 }
83+
}
84+
5185
/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
5286
/// data described via [`Layout`][].
5387
///
@@ -107,6 +141,13 @@ impl fmt::Display for AllocError {
107141
#[unstable(feature = "allocator_api", issue = "32838")]
108142
#[const_trait]
109143
pub unsafe trait Allocator {
144+
//const fn is_co_allocator() -> bool {false}
145+
// Can't have: const type Xyz;
146+
/// If this is any type with non-zero size, then the actual `Allocator` implementation supports cooperative functions (`co_*`) as first class citizens.
147+
//type IsCoAllocator = ();
148+
// It applies to the global (default) allocator only. And/or System allocator?! TODO
149+
const IS_CO_ALLOCATOR: bool = true;
150+
110151
/// Attempts to allocate a block of memory.
111152
///
112153
/// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
@@ -129,6 +170,8 @@ pub unsafe trait Allocator {
129170
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
130171
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
131172

173+
fn co_allocate(&self, _layout: Layout, _result: &mut SliceAndMetaResult) {panic!("TODO")}
174+
132175
/// Behaves like `allocate`, but also ensures that the returned memory is zero-initialized.
133176
///
134177
/// # Errors
@@ -151,6 +194,19 @@ pub unsafe trait Allocator {
151194
Ok(ptr)
152195
}
153196

197+
fn co_allocate_zeroed(&self, layout: Layout, mut result: &mut SliceAndMetaResult) {
198+
self.co_allocate(layout, &mut result);
199+
if let Ok(SliceAndMeta{slice, ..}) = result {
200+
// SAFETY: `alloc` returns a valid memory block
201+
unsafe {
202+
slice
203+
.as_non_null_ptr()
204+
.as_ptr()
205+
.write_bytes(0, slice.len())
206+
}
207+
}
208+
}
209+
154210
/// Deallocates the memory referenced by `ptr`.
155211
///
156212
/// # Safety
@@ -162,6 +218,8 @@ pub unsafe trait Allocator {
162218
/// [*fit*]: #memory-fitting
163219
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
164220

221+
unsafe fn co_deallocate(&self, _ptr_and_meta: PtrAndMeta, _layout: Layout) {panic!("TODO")}
222+
165223
/// Attempts to extend the memory block.
166224
///
167225
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
@@ -226,6 +284,37 @@ pub unsafe trait Allocator {
226284
Ok(new_ptr)
227285
}
228286

287+
unsafe fn co_grow(
288+
&self,
289+
ptr_and_meta: PtrAndMeta,
290+
old_layout: Layout,
291+
new_layout: Layout,
292+
mut result: &mut SliceAndMetaResult
293+
) {
294+
debug_assert!(
295+
new_layout.size() >= old_layout.size(),
296+
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
297+
);
298+
299+
self.co_allocate(new_layout, &mut result);
300+
301+
if let Ok(SliceAndMeta {slice, ..}) = result {
302+
// SAFETY: because `new_layout.size()` must be greater than or equal to
303+
// `old_layout.size()`, both the old and new memory allocation are valid for reads and
304+
// writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
305+
// deallocated, it cannot overlap `new_slice_and_meta.slice`. Thus, the call to `copy_nonoverlapping` is
306+
// safe. The safety contract for `dealloc` must be upheld by the caller.
307+
unsafe {
308+
ptr::copy_nonoverlapping(
309+
ptr_and_meta.ptr.as_ptr(),
310+
slice.as_mut_ptr(),
311+
old_layout.size(),
312+
);
313+
self.co_deallocate(ptr_and_meta, old_layout);
314+
}
315+
}
316+
}
317+
229318
/// Behaves like `grow`, but also ensures that the new contents are set to zero before being
230319
/// returned.
231320
///
@@ -289,6 +378,37 @@ pub unsafe trait Allocator {
289378
Ok(new_ptr)
290379
}
291380

381+
unsafe fn co_grow_zeroed(
382+
&self,
383+
ptr_and_meta: PtrAndMeta,
384+
old_layout: Layout,
385+
new_layout: Layout,
386+
mut result: &mut SliceAndMetaResult
387+
) {
388+
debug_assert!(
389+
new_layout.size() >= old_layout.size(),
390+
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
391+
);
392+
393+
self.co_allocate_zeroed(new_layout, &mut result);
394+
395+
if let Ok(SliceAndMeta{ slice, ..}) = result {
396+
// SAFETY: because `new_layout.size()` must be greater than or equal to
397+
// `old_layout.size()`, both the old and new memory allocation are valid for reads and
398+
// writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
399+
// deallocated, it cannot overlap `new_slice_and_meta.slice`. Thus, the call to `copy_nonoverlapping` is
400+
// safe. The safety contract for `dealloc` must be upheld by the caller.
401+
unsafe {
402+
ptr::copy_nonoverlapping(
403+
ptr_and_meta.ptr.as_ptr(),
404+
slice.as_mut_ptr(),
405+
old_layout.size(),
406+
);
407+
self.co_deallocate(ptr_and_meta, old_layout);
408+
}
409+
}
410+
}
411+
292412
/// Attempts to shrink the memory block.
293413
///
294414
/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
@@ -353,6 +473,37 @@ pub unsafe trait Allocator {
353473
Ok(new_ptr)
354474
}
355475

476+
unsafe fn co_shrink(
477+
&self,
478+
ptr_and_meta: PtrAndMeta,
479+
old_layout: Layout,
480+
new_layout: Layout,
481+
mut result: &mut SliceAndMetaResult
482+
) {
483+
debug_assert!(
484+
new_layout.size() <= old_layout.size(),
485+
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
486+
);
487+
488+
self.co_allocate(new_layout, &mut result);
489+
490+
if let Ok(SliceAndMeta{ slice, ..}) = result {
491+
// SAFETY: because `new_layout.size()` must be lower than or equal to
492+
// `old_layout.size()`, both the old and new memory allocation are valid for reads and
493+
// writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
494+
// deallocated, it cannot overlap `new_slice_and_meta.slice`. Thus, the call to `copy_nonoverlapping` is
495+
// safe. The safety contract for `dealloc` must be upheld by the caller.
496+
unsafe {
497+
ptr::copy_nonoverlapping(
498+
ptr_and_meta.ptr.as_ptr(),
499+
slice.as_mut_ptr(),
500+
new_layout.size(),
501+
);
502+
self.co_deallocate(ptr_and_meta, old_layout);
503+
}
504+
}
505+
}
506+
356507
/// Creates a "by reference" adapter for this instance of `Allocator`.
357508
///
358509
/// The returned adapter also implements `Allocator` and will simply borrow this.
@@ -365,6 +516,7 @@ pub unsafe trait Allocator {
365516
}
366517
}
367518

519+
// @TODO
368520
#[unstable(feature = "allocator_api", issue = "32838")]
369521
unsafe impl<A> Allocator for &A
370522
where

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
9999
//
100100
// Library features:
101+
#![feature(associated_type_defaults)]
101102
#![feature(const_align_offset)]
102103
#![feature(const_align_of_val)]
103104
#![feature(const_align_of_val_raw)]

library/std/src/alloc.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,13 @@ impl System {
197197
}
198198
}
199199

200+
// @TODO
200201
// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
201202
// which is in `std::sys::*::alloc`.
202203
#[unstable(feature = "allocator_api", issue = "32838")]
203204
unsafe impl Allocator for System {
205+
const IS_CO_ALLOCATOR: bool = false;
206+
204207
#[inline]
205208
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
206209
self.alloc_impl(layout, false)

0 commit comments

Comments
 (0)