feat: unsafely support ?Sized
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -24,9 +24,15 @@ checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
|||||||
name = "concurrent-linked-list"
|
name = "concurrent-linked-list"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dst-clone",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dst-clone"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://git.javalsai.tuxcord.net/tuxcord/dst-clone#6a32bfa59455adafde07bafd3bc9f2182cd5127b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.174"
|
version = "0.2.174"
|
||||||
@@ -68,9 +74,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.13"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
@@ -4,4 +4,5 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dst-clone = { git = "https://git.javalsai.tuxcord.net/tuxcord/dst-clone", version = "0" }
|
||||||
parking_lot = { version = "0", default-features = false }
|
parking_lot = { version = "0", default-features = false }
|
||||||
|
@@ -6,7 +6,7 @@ use std::{marker::PhantomPinned, mem::transmute, ops::Deref, pin::Pin};
|
|||||||
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::double::node::{BackNodeWriteLock, Node, NodeBackPtr};
|
use crate::double::node::{BackNodeWriteLock, Node};
|
||||||
|
|
||||||
pub mod node;
|
pub mod node;
|
||||||
|
|
||||||
@@ -16,26 +16,26 @@ pub mod node;
|
|||||||
// case the task holding the left lock is moving rightwards.
|
// case the task holding the left lock is moving rightwards.
|
||||||
// Rightwards locking can be blocking.
|
// Rightwards locking can be blocking.
|
||||||
|
|
||||||
pub struct NodeHeadInner<'ll, T> {
|
pub struct NodeHeadInner<'ll, T: ?Sized> {
|
||||||
start: Option<&'ll node::Node<'ll, T>>,
|
start: Option<&'ll node::Node<'ll, T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for NodeHeadInner<'_, T> {
|
impl<T: ?Sized> Default for NodeHeadInner<'_, T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { start: None }
|
Self { start: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NodeHead<'ll, T>(RwLock<NodeHeadInner<'ll, T>>);
|
pub struct NodeHead<'ll, T: ?Sized>(RwLock<NodeHeadInner<'ll, T>>);
|
||||||
|
|
||||||
impl<T> Default for NodeHead<'_, T> {
|
impl<T: ?Sized> Default for NodeHead<'_, T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(RwLock::new(NodeHeadInner::default()))
|
Self(RwLock::new(NodeHeadInner::default()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, T> Deref for NodeHead<'ll, T> {
|
impl<'ll, T: ?Sized> Deref for NodeHead<'ll, T> {
|
||||||
type Target = RwLock<NodeHeadInner<'ll, T>>;
|
type Target = RwLock<NodeHeadInner<'ll, T>>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -43,7 +43,7 @@ impl<'ll, T> Deref for NodeHead<'ll, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, T> NodeHead<'ll, T> {
|
impl<'ll, T: ?Sized> NodeHead<'ll, T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
@@ -85,13 +85,30 @@ impl<'ll, T> NodeHead<'ll, T> {
|
|||||||
while unsafe { self.pop().is_some() } {}
|
while unsafe { self.pop().is_some() } {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepend(&'ll self, data: T) {
|
pub fn prepend(&'ll self, data: T) -> &'ll Node<'ll, T>
|
||||||
|
where
|
||||||
|
T: Sized,
|
||||||
|
{
|
||||||
let self_lock = self.write();
|
let self_lock = self.write();
|
||||||
let next = self_lock.start;
|
let next = self_lock.start;
|
||||||
let next_lock = next.map(|n| n.write());
|
let next_lock = next.map(|n| n.ptrs.write());
|
||||||
let new_node = Node::new_leaked(data, NodeBackPtr::new_head(self), next);
|
let new_node = Node::new_leaked(data, node::NodeBackPtr::new_head(self), next);
|
||||||
// SAFETY: ptrs are surrounding and they've been locked all along
|
// SAFETY: ptrs are surrounding and they've been locked all along
|
||||||
unsafe { new_node.integrate((BackNodeWriteLock::Head(self_lock), next_lock)) };
|
unsafe { new_node.integrate((BackNodeWriteLock::Head(self_lock), next_lock)) };
|
||||||
|
new_node
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend_dst(&'ll self, data: &T) -> &'ll Node<'ll, T>
|
||||||
|
where
|
||||||
|
T: dst_clone::DstClone,
|
||||||
|
{
|
||||||
|
let self_lock = self.write();
|
||||||
|
let next = self_lock.start;
|
||||||
|
let next_lock = next.map(|n| n.ptrs.write());
|
||||||
|
let new_node = Node::leaked_from_dst(data, node::NodeBackPtr::new_head(self), next);
|
||||||
|
// SAFETY: ptrs are surrounding and they've been locked all along
|
||||||
|
unsafe { new_node.integrate((BackNodeWriteLock::Head(self_lock), next_lock)) };
|
||||||
|
new_node
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [`None`] if there's no next node.
|
/// Returns [`None`] if there's no next node.
|
||||||
@@ -102,8 +119,8 @@ impl<'ll, T> NodeHead<'ll, T> {
|
|||||||
pub unsafe fn pop(&'ll self) -> Option<()> {
|
pub unsafe fn pop(&'ll self) -> Option<()> {
|
||||||
let self_lock = self.write();
|
let self_lock = self.write();
|
||||||
let pop_node = self_lock.start?;
|
let pop_node = self_lock.start?;
|
||||||
let pop_node_lock = pop_node.write();
|
let pop_node_lock = pop_node.ptrs.write();
|
||||||
let next_node_lock = pop_node_lock.next.map(|n| n.write());
|
let next_node_lock = pop_node_lock.next.map(|n| n.ptrs.write());
|
||||||
|
|
||||||
// SAFETY: locked all along and consecutive nodes
|
// SAFETY: locked all along and consecutive nodes
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -115,6 +132,7 @@ impl<'ll, T> NodeHead<'ll, T> {
|
|||||||
|
|
||||||
drop(pop_node_lock);
|
drop(pop_node_lock);
|
||||||
// SAFETY: node has been isolated so no references out
|
// SAFETY: node has been isolated so no references out
|
||||||
|
// TODO: return a droppable guard with a ptr to the node instead
|
||||||
unsafe { pop_node.wait_free() }
|
unsafe { pop_node.wait_free() }
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
@@ -127,8 +145,8 @@ impl<'ll, T> NodeHead<'ll, T> {
|
|||||||
let mut total = Vec::new();
|
let mut total = Vec::new();
|
||||||
let mut next_node = self.read().start;
|
let mut next_node = self.read().start;
|
||||||
while let Some(node) = next_node {
|
while let Some(node) = next_node {
|
||||||
let read = node.read();
|
let read = node.ptrs.read();
|
||||||
total.push(read.data.clone());
|
total.push(node.data.clone());
|
||||||
next_node = read.next;
|
next_node = read.next;
|
||||||
}
|
}
|
||||||
total
|
total
|
||||||
@@ -140,12 +158,12 @@ impl<'ll, T> NodeHead<'ll, T> {
|
|||||||
/// Attempt to safe wrap around a [`NodeHead`] to allow a sound API with [`Drop`] implementation.
|
/// Attempt to safe wrap around a [`NodeHead`] to allow a sound API with [`Drop`] implementation.
|
||||||
///
|
///
|
||||||
/// Please see [`create_ll`] and [`del_ll`]
|
/// Please see [`create_ll`] and [`del_ll`]
|
||||||
pub struct LinkedList<'ll, T> {
|
pub struct LinkedList<'ll, T: ?Sized> {
|
||||||
head: NodeHead<'ll, T>,
|
head: NodeHead<'ll, T>,
|
||||||
_pinned: PhantomPinned,
|
_pinned: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, T> LinkedList<'ll, T> {
|
impl<'ll, T: ?Sized> LinkedList<'ll, T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
@@ -166,7 +184,7 @@ impl<'ll, T> LinkedList<'ll, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for LinkedList<'_, T> {
|
impl<T: ?Sized> Default for LinkedList<'_, T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -176,7 +194,7 @@ impl<T> Default for LinkedList<'_, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for LinkedList<'_, T> {
|
impl<T: ?Sized> Drop for LinkedList<'_, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Extend to 'static so the compiler doesn't cry, we know this covers 'll
|
// Extend to 'static so the compiler doesn't cry, we know this covers 'll
|
||||||
let myself = unsafe { self.extend_for(&()) };
|
let myself = unsafe { self.extend_for(&()) };
|
||||||
@@ -227,8 +245,8 @@ impl<T> Drop for LinkedList<'_, T> {
|
|||||||
/// ll.prepend("test2".to_string());
|
/// ll.prepend("test2".to_string());
|
||||||
/// ```
|
/// ```
|
||||||
pub macro create_ll($rhs:expr, $val:ident, $ref:ident) {
|
pub macro create_ll($rhs:expr, $val:ident, $ref:ident) {
|
||||||
let $val = $rhs;
|
|
||||||
let scope = ();
|
let scope = ();
|
||||||
|
let $val = $rhs;
|
||||||
let $ref = unsafe { $val.extend_for(&scope) };
|
let $ref = unsafe { $val.extend_for(&scope) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
use std::{ops::Deref, ptr};
|
use std::{
|
||||||
|
alloc::{Layout, alloc, handle_alloc_error},
|
||||||
|
ops::Deref,
|
||||||
|
ptr::{self, metadata},
|
||||||
|
};
|
||||||
|
|
||||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard};
|
use parking_lot::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use super::NodeHeadInner;
|
use super::NodeHeadInner;
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Node<'ll, T>(RwLock<NodeInner<'ll, T>>);
|
|
||||||
type NodeHead<'ll, T> = RwLock<NodeHeadInner<'ll, T>>;
|
type NodeHead<'ll, T> = RwLock<NodeHeadInner<'ll, T>>;
|
||||||
pub enum NodeBackPtr<'ll, T> {
|
pub enum NodeBackPtr<'ll, T: ?Sized> {
|
||||||
Head(&'ll NodeHead<'ll, T>),
|
Head(&'ll NodeHead<'ll, T>),
|
||||||
Node(&'ll Node<'ll, T>),
|
Node(&'ll Node<'ll, T>),
|
||||||
}
|
}
|
||||||
@@ -15,27 +17,18 @@ pub enum NodeBackPtr<'ll, T> {
|
|||||||
// yes the whole purpose is docs, might add Isolated Guards around nodes here
|
// yes the whole purpose is docs, might add Isolated Guards around nodes here
|
||||||
pub mod topology_safety;
|
pub mod topology_safety;
|
||||||
|
|
||||||
// TODO: RwLock the ptrs only instead of the node
|
pub struct NodePtrPair<'ll, T: ?Sized> {
|
||||||
// Box<(RwLock<(&prev, &next)>, T)>
|
pub prev: NodeBackPtr<'ll, T>,
|
||||||
// instead of
|
pub next: Option<&'ll Node<'ll, T>>,
|
||||||
// Box<RwLock<(&prev, &next, T)>>
|
}
|
||||||
// allows user to opt out of RwLock, allowing changes to adyacent nodes while T is being externally
|
|
||||||
// used and enables T: ?Sized
|
#[repr(C)]
|
||||||
pub struct NodeInner<'ll, T> {
|
pub struct Node<'ll, T: ?Sized> {
|
||||||
pub(crate) prev: NodeBackPtr<'ll, T>,
|
pub ptrs: RwLock<NodePtrPair<'ll, T>>,
|
||||||
pub(crate) next: Option<&'ll Node<'ll, T>>,
|
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, T> Deref for Node<'ll, T> {
|
impl<T: ?Sized> Deref for Node<'_, T> {
|
||||||
type Target = RwLock<NodeInner<'ll, T>>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Deref for NodeInner<'_, T> {
|
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -43,28 +36,27 @@ impl<T> Deref for NodeInner<'_, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Copy for NodeBackPtr<'_, T> {}
|
impl<T: ?Sized> Copy for NodeBackPtr<'_, T> {}
|
||||||
impl<T> Clone for NodeBackPtr<'_, T> {
|
impl<T: ?Sized> Clone for NodeBackPtr<'_, T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type WriteAndBackDoublet<'ll, T> = (
|
type WriteAndBackDoublet<'ll, T> = (
|
||||||
BackNodeWriteLock<'ll, T>,
|
BackNodeWriteLock<'ll, T>,
|
||||||
RwLockUpgradableReadGuard<'ll, NodeInner<'ll, T>>,
|
RwLockUpgradableReadGuard<'ll, NodePtrPair<'ll, T>>,
|
||||||
);
|
);
|
||||||
type WriteSurroundTriplet<'ll, T> = (
|
type WriteSurroundTriplet<'ll, T> = (
|
||||||
BackNodeWriteLock<'ll, T>,
|
BackNodeWriteLock<'ll, T>,
|
||||||
RwLockWriteGuard<'ll, NodeInner<'ll, T>>,
|
RwLockWriteGuard<'ll, NodePtrPair<'ll, T>>,
|
||||||
Option<RwLockWriteGuard<'ll, NodeInner<'ll, T>>>,
|
Option<RwLockWriteGuard<'ll, NodePtrPair<'ll, T>>>,
|
||||||
);
|
);
|
||||||
type WriteOnlyAroundTriplet<'ll, T> = (
|
type WriteOnlyAroundTriplet<'ll, T> = (
|
||||||
BackNodeWriteLock<'ll, T>,
|
BackNodeWriteLock<'ll, T>,
|
||||||
Option<RwLockWriteGuard<'ll, NodeInner<'ll, T>>>,
|
Option<RwLockWriteGuard<'ll, NodePtrPair<'ll, T>>>,
|
||||||
);
|
);
|
||||||
impl<'ll, T> Node<'ll, T> {
|
|
||||||
// TODO: think about the isolaed state of the following 3 fn's
|
|
||||||
|
|
||||||
|
impl<'ll, T> Node<'ll, T> {
|
||||||
/// Creates a new node in the heap, will link to `prev` and `next` but will be isolated, it can
|
/// Creates a new node in the heap, will link to `prev` and `next` but will be isolated, it can
|
||||||
/// be thought of just ita data and the two pointers, having it isolated doesn't guarantee any
|
/// be thought of just ita data and the two pointers, having it isolated doesn't guarantee any
|
||||||
/// integration into the linked list.
|
/// integration into the linked list.
|
||||||
@@ -73,19 +65,10 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
/// considered that the `prev` and `next` refs are being held.
|
/// considered that the `prev` and `next` refs are being held.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(data: T, prev: NodeBackPtr<'ll, T>, next: Option<&'ll Node<'ll, T>>) -> Self {
|
pub fn new(data: T, prev: NodeBackPtr<'ll, T>, next: Option<&'ll Node<'ll, T>>) -> Self {
|
||||||
Self(RwLock::new(NodeInner { prev, next, data }))
|
Self {
|
||||||
|
ptrs: RwLock::new(NodePtrPair { prev, next }),
|
||||||
|
data,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boxes [`self`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn boxed(self) -> Box<Self> {
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Leaks [`self`] as a [`Box<Self>`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn leak(self: Box<Self>) -> &'static mut Self {
|
|
||||||
Box::leak(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_leaked(
|
pub fn new_leaked(
|
||||||
@@ -96,6 +79,52 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
Box::leak(Box::new(Self::new(data, prev, next)))
|
Box::leak(Box::new(Self::new(data, prev, next)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Boxes [`self`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn boxed(self) -> Box<Self> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ll, T: ?Sized> Node<'ll, T> {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// On arithmetic overflow
|
||||||
|
fn layout_for(data: &T) -> (Layout, usize) {
|
||||||
|
Layout::new::<RwLock<NodePtrPair<'ll, T>>>()
|
||||||
|
.extend(Layout::for_value(data))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`Self::new_leaked`] from a DST type
|
||||||
|
pub fn leaked_from_dst(
|
||||||
|
data: &T,
|
||||||
|
prev: NodeBackPtr<'ll, T>,
|
||||||
|
next: Option<&'ll Node<'ll, T>>,
|
||||||
|
) -> &'ll mut Self
|
||||||
|
where
|
||||||
|
T: dst_clone::DstClone,
|
||||||
|
{
|
||||||
|
let (layout, data_offset) = Self::layout_for(data);
|
||||||
|
let ptr = unsafe { alloc(layout) };
|
||||||
|
if ptr.is_null() {
|
||||||
|
handle_alloc_error(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*(ptr.cast::<RwLock<NodePtrPair<'ll, T>>>()) = RwLock::new(NodePtrPair { prev, next });
|
||||||
|
data.copy(ptr.add(data_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::leak(unsafe { Box::from_raw(ptr::from_raw_parts_mut(ptr, metadata(data))) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Leaks [`self`] as a [`Box<Self>`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn leak(self: Box<Self>) -> &'static mut Self {
|
||||||
|
Box::leak(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The [`self`] pointer must come from a [`Box`] allocation like [`Self::boxed`] and
|
/// The [`self`] pointer must come from a [`Box`] allocation like [`Self::boxed`] and
|
||||||
@@ -111,8 +140,8 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
/// There must be no references left to [`self`]
|
/// There must be no references left to [`self`]
|
||||||
pub unsafe fn wait_free(&self) {
|
pub unsafe fn wait_free(&self) {
|
||||||
loop {
|
loop {
|
||||||
if self.is_locked() {
|
if self.ptrs.is_locked() {
|
||||||
drop(self.write());
|
drop(self.ptrs.write());
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -123,7 +152,7 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock_and_back(&'ll self) -> WriteAndBackDoublet<'ll, T> {
|
pub fn lock_and_back(&'ll self) -> WriteAndBackDoublet<'ll, T> {
|
||||||
let mut self_read = self.upgradable_read();
|
let mut self_read = self.ptrs.upgradable_read();
|
||||||
// "soft" back lock
|
// "soft" back lock
|
||||||
match self_read.prev.try_write() {
|
match self_read.prev.try_write() {
|
||||||
Some(prev_write) => (prev_write, self_read),
|
Some(prev_write) => (prev_write, self_read),
|
||||||
@@ -154,7 +183,7 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
let (prev_write, self_read) = self.lock_and_back();
|
let (prev_write, self_read) = self.lock_and_back();
|
||||||
// Now `prev` is write locked and we can block forwards
|
// Now `prev` is write locked and we can block forwards
|
||||||
let self_write = RwLockUpgradableReadGuard::upgrade(self_read);
|
let self_write = RwLockUpgradableReadGuard::upgrade(self_read);
|
||||||
let next_write = self_write.next.map(|n| n.write());
|
let next_write = self_write.next.map(|n| n.ptrs.write());
|
||||||
|
|
||||||
(prev_write, self_write, next_write)
|
(prev_write, self_write, next_write)
|
||||||
}
|
}
|
||||||
@@ -164,7 +193,7 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
/// The passed locks must also be consecutive for this to respect topology.
|
/// The passed locks must also be consecutive for this to respect topology.
|
||||||
///
|
///
|
||||||
/// This node will remain isolated. See [`topology_safety`].
|
/// This node will remain isolated. See [`topology_safety`].
|
||||||
pub unsafe fn isolate(self_read: &NodeInner<'ll, T>, locks: WriteOnlyAroundTriplet<'ll, T>) {
|
pub unsafe fn isolate(self_read: &NodePtrPair<'ll, T>, locks: WriteOnlyAroundTriplet<'ll, T>) {
|
||||||
let (mut back_write, next_write) = locks;
|
let (mut back_write, next_write) = locks;
|
||||||
|
|
||||||
back_write.set_next(self_read.next);
|
back_write.set_next(self_read.next);
|
||||||
@@ -209,19 +238,19 @@ impl<'ll, T> Node<'ll, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generic Write Lock of a [`NodeBackPtr`]
|
/// Generic Write Lock of a [`NodeBackPtr`]
|
||||||
pub enum BackNodeWriteLock<'ll, T> {
|
pub enum BackNodeWriteLock<'ll, T: ?Sized> {
|
||||||
Head(RwLockWriteGuard<'ll, NodeHeadInner<'ll, T>>),
|
Head(RwLockWriteGuard<'ll, NodeHeadInner<'ll, T>>),
|
||||||
Node(RwLockWriteGuard<'ll, NodeInner<'ll, T>>),
|
Node(RwLockWriteGuard<'ll, NodePtrPair<'ll, T>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic Read Lock of a [`NodeBackPtr`]
|
/// Generic Read Lock of a [`NodeBackPtr`]
|
||||||
pub enum BackNodeReadLock<'ll, T> {
|
pub enum BackNodeReadLock<'ll, T: ?Sized> {
|
||||||
Head(RwLockReadGuard<'ll, NodeHeadInner<'ll, T>>),
|
Head(RwLockReadGuard<'ll, NodeHeadInner<'ll, T>>),
|
||||||
Node(RwLockReadGuard<'ll, NodeInner<'ll, T>>),
|
Node(RwLockReadGuard<'ll, NodePtrPair<'ll, T>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::enum_glob_use)]
|
#[allow(clippy::enum_glob_use)]
|
||||||
impl<'ll, T> NodeBackPtr<'ll, T> {
|
impl<'ll, T: ?Sized> NodeBackPtr<'ll, T> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn ptr_eq(&self, other: &Self) -> bool {
|
pub fn ptr_eq(&self, other: &Self) -> bool {
|
||||||
use NodeBackPtr::*;
|
use NodeBackPtr::*;
|
||||||
@@ -250,7 +279,7 @@ impl<'ll, T> NodeBackPtr<'ll, T> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Head(h) => WL::Head(h.write()),
|
Head(h) => WL::Head(h.write()),
|
||||||
Node(n) => WL::Node(n.write()),
|
Node(n) => WL::Node(n.ptrs.write()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +291,7 @@ impl<'ll, T> NodeBackPtr<'ll, T> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Head(h) => RL::Head(h.read()),
|
Head(h) => RL::Head(h.read()),
|
||||||
Node(n) => RL::Node(n.read()),
|
Node(n) => RL::Node(n.ptrs.read()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +303,7 @@ impl<'ll, T> NodeBackPtr<'ll, T> {
|
|||||||
|
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Head(h) => WL::Head(h.try_write()?),
|
Head(h) => WL::Head(h.try_write()?),
|
||||||
Node(n) => WL::Node(n.try_write()?),
|
Node(n) => WL::Node(n.ptrs.try_write()?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,12 +315,12 @@ impl<'ll, T> NodeBackPtr<'ll, T> {
|
|||||||
|
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Head(h) => RL::Head(h.try_read()?),
|
Head(h) => RL::Head(h.try_read()?),
|
||||||
Node(n) => RL::Node(n.try_read()?),
|
Node(n) => RL::Node(n.ptrs.try_read()?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ll, T> BackNodeWriteLock<'ll, T> {
|
impl<'ll, T: ?Sized> BackNodeWriteLock<'ll, T> {
|
||||||
#[allow(clippy::enum_glob_use)]
|
#[allow(clippy::enum_glob_use)]
|
||||||
fn set_next(&mut self, next: Option<&'ll Node<'ll, T>>) {
|
fn set_next(&mut self, next: Option<&'ll Node<'ll, T>>) {
|
||||||
use BackNodeWriteLock::*;
|
use BackNodeWriteLock::*;
|
||||||
@@ -303,4 +332,4 @@ impl<'ll, T> BackNodeWriteLock<'ll, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> NodeInner<'_, T> {}
|
impl<T> Node<'_, T> {}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
ptr,
|
||||||
sync::{
|
sync::{
|
||||||
Barrier,
|
Barrier,
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
@@ -11,6 +12,8 @@ use std::{
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn concurrency_and_scoped_drop() {
|
||||||
static DROP_C: AtomicUsize = AtomicUsize::new(0);
|
static DROP_C: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -34,8 +37,6 @@ impl Debug for StringWithDrop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn concurrency_and_scoped_drop() {
|
|
||||||
const CREATE1: usize = 100;
|
const CREATE1: usize = 100;
|
||||||
const CREATE2: usize = 100;
|
const CREATE2: usize = 100;
|
||||||
const NOT_POP: usize = 20;
|
const NOT_POP: usize = 20;
|
||||||
@@ -79,3 +80,16 @@ fn concurrency_and_scoped_drop() {
|
|||||||
assert_eq!(DROP_C.load(Ordering::Relaxed), CREATE1 + CREATE2);
|
assert_eq!(DROP_C.load(Ordering::Relaxed), CREATE1 + CREATE2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsized_store() {
|
||||||
|
create_ll!(LinkedList::<str>::new(), ll_val, ll);
|
||||||
|
|
||||||
|
let inserted = ll.prepend_dst("test");
|
||||||
|
let first_node = ll.0.read().start.expect("first node to be some");
|
||||||
|
assert!(ptr::addr_eq(inserted, first_node));
|
||||||
|
|
||||||
|
assert_eq!(&**inserted, "test");
|
||||||
|
|
||||||
|
del_ll!(ll_val, ll);
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![feature(arbitrary_self_types, arbitrary_self_types_pointers, decl_macro)]
|
#![feature(
|
||||||
|
arbitrary_self_types,
|
||||||
|
arbitrary_self_types_pointers,
|
||||||
|
decl_macro,
|
||||||
|
ptr_metadata
|
||||||
|
)]
|
||||||
#![warn(clippy::pedantic)]
|
#![warn(clippy::pedantic)]
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
|
Reference in New Issue
Block a user