mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-09-27 04:20:24 +00:00
add Arc pointer backed by a memory pool
This commit is contained in:
parent
59bc89f297
commit
d963a0ef6d
@ -79,6 +79,8 @@ pub use histbuf::HistoryBuffer;
|
||||
pub use indexmap::{Bucket, FnvIndexMap, IndexMap, Pos};
|
||||
pub use indexset::{FnvIndexSet, IndexSet};
|
||||
pub use linear_map::LinearMap;
|
||||
#[cfg(all(has_cas, feature = "cas"))]
|
||||
pub use pool::singleton::arc::Arc;
|
||||
pub use string::String;
|
||||
pub use vec::Vec;
|
||||
|
||||
|
@ -12,6 +12,8 @@ use core::{
|
||||
|
||||
use super::{Init, Node, Uninit};
|
||||
|
||||
pub mod arc;
|
||||
|
||||
/// Instantiates a pool as a global singleton
|
||||
// NOTE(any(test)) makes testing easier (no need to enable Cargo features for testing)
|
||||
#[cfg(any(
|
||||
|
299
src/pool/singleton/arc.rs
Normal file
299
src/pool/singleton/arc.rs
Normal file
@ -0,0 +1,299 @@
|
||||
//! `std::sync::Arc` but backed by a memory `Pool` rather than `#[global_allocator]`
|
||||
|
||||
use core::{
|
||||
cmp, fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
ptr,
|
||||
sync::atomic::{self, AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use crate::pool::{self, stack::Ptr, Node};
|
||||
|
||||
/// Instantiates a pool as a global singleton
|
||||
// NOTE(any(test)) makes testing easier (no need to enable Cargo features for testing)
|
||||
#[cfg(any(
|
||||
armv7a,
|
||||
armv7r,
|
||||
armv7m,
|
||||
armv8m_main,
|
||||
all(
|
||||
any(target_arch = "x86_64", target_arch = "x86"),
|
||||
feature = "x86-sync-pool"
|
||||
),
|
||||
test
|
||||
))]
|
||||
#[macro_export]
|
||||
macro_rules! arc_pool {
|
||||
($(#[$($attr:tt)*])* $ident:ident: $ty:ty) => {
|
||||
pub struct $ident;
|
||||
|
||||
impl $crate::pool::singleton::arc::Pool for $ident {
|
||||
type Data = $ty;
|
||||
|
||||
fn ptr() -> &'static $crate::pool::Pool<$crate::pool::singleton::arc::ArcInner<$ty>> {
|
||||
$(#[$($attr)*])*
|
||||
static POOL: $crate::pool::Pool<$crate::pool::singleton::arc::ArcInner<$ty>> =
|
||||
$crate::pool::Pool::new();
|
||||
|
||||
&POOL
|
||||
}
|
||||
}
|
||||
|
||||
impl $ident {
|
||||
/// Allocates a new `Arc` and writes `data` to it
|
||||
///
|
||||
/// Returns an `Err`or if the backing memory pool is empty
|
||||
pub fn alloc(data: $ty) -> Result<$crate::Arc<Self>, $ty>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
$crate::Arc::new(data)
|
||||
}
|
||||
|
||||
/// Increases the capacity of the pool
|
||||
///
|
||||
/// This method might *not* fully utilize the given memory block due to alignment requirements
|
||||
///
|
||||
/// This method returns the number of *new* blocks that can be allocated.
|
||||
pub fn grow(memory: &'static mut [u8]) -> usize {
|
||||
<Self as $crate::pool::singleton::arc::Pool>::ptr().grow(memory)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Pool of Arc pointers
|
||||
pub trait Pool {
|
||||
/// The data behind the Arc pointer
|
||||
type Data: 'static;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn ptr() -> &'static pool::Pool<ArcInner<Self::Data>>;
|
||||
}
|
||||
|
||||
// mostly a verbatim copy of liballoc(/src/sync.rs) v1.54.0 minus the `Weak` API
|
||||
// anything that diverges has been marked with `XXX`
|
||||
|
||||
/// `std::sync::Arc` but backed by a memory `Pool` rather than `#[global_allocator]`
|
||||
// XXX `Pool::Data` is not `?Sized` -- `Unsize` coercions cannot be implemented on stable
|
||||
pub struct Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
phantom: PhantomData<ArcInner<P::Data>>,
|
||||
ptr: Ptr<Node<ArcInner<P::Data>>>,
|
||||
pool: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P> Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
/// Constructs a new `Arc`
|
||||
///
|
||||
/// Returns an `Err`or if the backing memory pool is empty
|
||||
// XXX original API is "infallible"
|
||||
pub fn new(data: P::Data) -> Result<Self, P::Data> {
|
||||
if let Some(node) = P::ptr().stack.try_pop() {
|
||||
unsafe {
|
||||
ptr::write(
|
||||
node.as_ref().data.get(),
|
||||
ArcInner {
|
||||
strong: AtomicUsize::new(1),
|
||||
data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
phantom: PhantomData,
|
||||
pool: PhantomData,
|
||||
ptr: node,
|
||||
})
|
||||
} else {
|
||||
Err(data)
|
||||
}
|
||||
}
|
||||
|
||||
fn inner(&self) -> &ArcInner<P::Data> {
|
||||
unsafe { &*self.ptr.as_ref().data.get() }
|
||||
}
|
||||
|
||||
fn from_inner(ptr: Ptr<Node<ArcInner<P::Data>>>) -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
pool: PhantomData,
|
||||
ptr,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_mut_unchecked(this: &mut Self) -> &mut P::Data {
|
||||
&mut (*this.ptr.as_ref().data.get()).data
|
||||
// &mut (*this.ptr.as_ptr()).data
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
unsafe fn drop_slow(&mut self) {
|
||||
// run `P::Data`'s destructor
|
||||
ptr::drop_in_place(Self::get_mut_unchecked(self));
|
||||
|
||||
// XXX memory pool instead of `#[global_allocator]`
|
||||
// return memory to pool
|
||||
P::ptr().stack.push(self.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
|
||||
|
||||
impl<P> AsRef<P::Data> for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
fn as_ref(&self) -> &P::Data {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
// XXX no `Borrow` implementation due to 'conflicting implementations of trait' error
|
||||
|
||||
impl<P> Clone for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
let old_size = self.inner().strong.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
if old_size > MAX_REFCOUNT {
|
||||
// XXX original code calls `intrinsics::abort` which is unstable API
|
||||
panic!();
|
||||
}
|
||||
|
||||
Self::from_inner(self.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> fmt::Debug for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Deref for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
type Target = P::Data;
|
||||
|
||||
fn deref(&self) -> &P::Data {
|
||||
&self.inner().data
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> fmt::Display for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
// XXX original uses `#[may_dangle]` which is an unstable language feature
|
||||
impl<P> Drop for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if self.inner().strong.fetch_sub(1, Ordering::Release) != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic::fence(Ordering::Acquire);
|
||||
|
||||
unsafe {
|
||||
self.drop_slow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Eq for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: Eq,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P> Hash for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: Hash,
|
||||
{
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
(**self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Ord for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> PartialEq for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// XXX missing pointer equality specialization
|
||||
(**self).eq(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> PartialOrd for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<P> Send for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: Sync + Send,
|
||||
{
|
||||
}
|
||||
|
||||
unsafe impl<P> Sync for Arc<P>
|
||||
where
|
||||
P: Pool,
|
||||
P::Data: Sync + Send,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P> Unpin for Arc<P> where P: Pool {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ArcInner<T> {
|
||||
data: T,
|
||||
strong: AtomicUsize,
|
||||
// XXX `Weak` API not implemented
|
||||
// weak: AtomicUsize,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user