Skip to content

Commit

Permalink
support-for-concurrency
Browse files Browse the repository at this point in the history
# Support for Concurrency

In version 2, PinnedVec grew with new methods to support concurrent data structures. However, this caused problems since these exposed methods were often unsafe, and further, they were not directly useful for the pinned vector consumers except for concurrent data structures wrapping a pinned vector. Furthermore, they are alien to a regular vector interface that we are used to using.

In version 3, a second trait called `ConcurrentPinnedVec` is defined. All useful methods related with concurrent programming are moved to this trait. This trait has an associated type defining the underlying pinned vector type. It can be turned into the pinned vector.

Finally, `IntoConcurrentPinnedVec` trait is defined. A pinned vector implementing this trait can be turned into a `ConcurrentPinnedVec`. As explained above, it can be converted back to the pinned vector.

This bi-directional transformation allows to wrap pinned vector to have concurrent support, and unwrap whenever concurrency is not required anymore.

An important advantage of this approach is that it allowed to clean up the `PinnedVec` api from unsafe and alien concurrency related methods.

# Also

Tests using clock are revised so that currently pinned vector tests are **miri safe**.

`PseudoDefault` is required for all pinned vectors. Note that a `FixedVec` cannot implement a `Default`, but as any type, it can implement a pseudo-default, which is also required for concurrent wrappers.
  • Loading branch information
Ugur Arikan (DHL Data & Analytics) committed Jul 23, 2024
1 parent 1694406 commit f536ba2
Show file tree
Hide file tree
Showing 19 changed files with 704 additions and 690 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "orx-split-vec"
version = "2.14.0"
version = "3.0.0"
edition = "2021"
authors = ["orxfun <[email protected]>"]
description = "An efficient constant access time vector with dynamic capacity and pinned elements."
Expand All @@ -10,7 +10,8 @@ keywords = ["vec", "array", "split", "fragments", "pinned"]
categories = ["data-structures", "rust-patterns"]

[dependencies]
orx-pinned-vec = "2.12"
orx-pseudo-default = "1.0"
orx-pinned-vec = "3.0"

[[bench]]
name = "serial_access"
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,19 @@ use orx_split_vec::*;

#[derive(Clone)]
struct MyCustomGrowth;

impl Growth for MyCustomGrowth {
fn new_fragment_capacity<T>(&self, fragments: &[Fragment<T>]) -> usize {
fragments.last().map(|f| f.capacity() + 1).unwrap_or(4)
fn new_fragment_capacity_from(&self, fragment_capacities: impl ExactSizeIterator<Item = usize>) -> usize {
fragment_capacities.last().map(|f| f + 1).unwrap_or(4)
}
}

impl PseudoDefault for MyCustomGrowth {
fn pseudo_default() -> Self {
MyCustomGrowth
}
}

// set the growth explicitly
let vec: SplitVec<i32, Linear> = SplitVec::with_linear_growth(4);
let vec: SplitVec<i32, Doubling> = SplitVec::with_doubling_growth();
Expand Down
69 changes: 69 additions & 0 deletions src/common_traits/iterator/iter_con.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use super::iter_fragment::FragmentIter;
use crate::fragment::fragment_struct::Fragment;
use std::iter::FusedIterator;

/// Iterator over the `SplitVec`.
///
/// This struct is created by `SplitVec::iter()` method.
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IterCon<'a, T> {
num_fragments: usize,
last_fragment_len: usize,
fragments: &'a [Fragment<T>],
inner: FragmentIter<'a, T>,
f: usize,
}

impl<'a, T> IterCon<'a, T> {
pub(crate) fn new(fragments: &'a [Fragment<T>], last_fragment_len: usize) -> Self {
assert!(fragments.len() > 0);

let num_fragments = fragments.len();

let first_fragment_len = match num_fragments {
0 => 0,
1 => last_fragment_len,
_ => fragments[0].capacity(),
};
let inner = FragmentIter::new(&fragments[0], first_fragment_len);

Self {
num_fragments,
last_fragment_len,
fragments,
inner,
f: 0,
}
}

fn next_fragment(&mut self) -> Option<&'a T> {
match self.f + 1 < self.num_fragments {
true => {
self.f += 1;
let fragment_len = match self.f == self.num_fragments - 1 {
false => self.fragments[self.f].capacity(),
true => self.last_fragment_len,
};
self.inner = FragmentIter::new(&self.fragments[self.f], fragment_len);
self.next()
}
false => None,
}
}
}

impl<'a, T> Iterator for IterCon<'a, T> {
type Item = &'a T;

#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let next_element = self.inner.next();
match next_element.is_some() {
true => next_element,
false => self.next_fragment(),
}
}
}

impl<T> FusedIterator for IterCon<'_, T> {}
35 changes: 35 additions & 0 deletions src/common_traits/iterator/iter_fragment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::Fragment;
use std::marker::PhantomData;

#[derive(Debug, Clone)]
pub(crate) struct FragmentIter<'a, T> {
fragment: &'a [T],
i: usize,
phantom: PhantomData<&'a ()>,
}

impl<'a, T> FragmentIter<'a, T> {
pub(crate) fn new(fragment: &'a Fragment<T>, len: usize) -> Self {
let fragment = unsafe { std::slice::from_raw_parts(fragment.as_ptr(), len) };
Self {
fragment,
i: 0,
phantom: PhantomData,
}
}
}

impl<'a, T: 'a> Iterator for FragmentIter<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<Self::Item> {
match self.i < self.fragment.len() {
true => {
let element = unsafe { self.fragment.get_unchecked(self.i) };
self.i += 1;
Some(element)
}
false => None,
}
}
}
2 changes: 2 additions & 0 deletions src/common_traits/iterator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ mod eq;
mod from_iter;
pub(crate) mod into_iter;
pub(crate) mod iter;
pub(crate) mod iter_con;
mod iter_fragment;
pub(crate) mod iter_mut;
pub(crate) mod iter_mut_rev;
pub(crate) mod iter_rev;
Expand Down
Loading

0 comments on commit f536ba2

Please sign in to comment.