This is a component that I wanted to extract from `netmod-tcp`, in the qaul.net tree. I was considering just moving this lib to the utils tree in the qaul.net repo, but I decided that I needed this library in too many other programs, and that it really didn't have much to do with qaul.net so it's living here now. Considering that I want to also convey stability in the API, I'll immediately bump it to 1.0.0 as well.wip/yesman
parent
81dbf21e0b
commit
4c4d16d58c
@ -0,0 +1 @@ |
||||
eval "$(lorri direnv)" |
@ -0,0 +1,4 @@ |
||||
|
||||
/target/ |
||||
**/*.rs.bk |
||||
Cargo.lock |
@ -0,0 +1,11 @@ |
||||
[package] |
||||
name = "atomptr" |
||||
description = "A safe, dependency-less abstraction for typed atomic smart pointers" |
||||
version = "1.0.0" |
||||
authors = ["Mx Kookie <kookie@spacekookie.de>"] |
||||
edition = "2018" |
||||
license = "GPL-3.0-or-later" |
||||
repository = "https://git.spacekookie.de/kookienomicon" |
||||
keywords = ["memory", "concurrency", "performance", "lock-free", "atomic"] |
||||
categories = ["concurrency", "memory-management"] |
||||
readme = "README.md" |
@ -0,0 +1 @@ |
||||
../../../licenses/GPL-3.0 |
@ -0,0 +1,51 @@ |
||||
# AtomPtr |
||||
|
||||
A safe, strongly typed (generic) atomic pointer abstraction to build |
||||
datastructures, and lock-free algorithms on top of. Only uses |
||||
`libstd`. |
||||
|
||||
The standard library contains an `AtomicPtr` type, which by itself |
||||
isn't very ergonomic to use, because it deals with raw pointers. This |
||||
library assumes that types can always be heap allocated, wrapping them |
||||
in a `Box<T>`, and provides a nicer (and safe!) abstraction for |
||||
`std::sync::atomic::AtomicPtr`. Using this crate is fairely |
||||
self-explanatory: |
||||
|
||||
```rust |
||||
use atomptr::AtomPtr; |
||||
|
||||
struct MyData { name: String } |
||||
let data = MyData { name: "Kookie".into() }; |
||||
|
||||
let a = AtomPtr::new(data); |
||||
println!("Name is: {}", a.get_ref().name); |
||||
|
||||
let old_ref = a.swap(MyData { name: "Bob".into() }); |
||||
println!("Name now is: {}, was {}", a.get_ref().name, old_ref.name); |
||||
``` |
||||
|
||||
Note that the type that is returned by `get_ref` and `swap` is |
||||
`Ref<T>`, which means that the old data is not de-allocated after a |
||||
swap, before this last reference goes out of scope. You can of course |
||||
always manually call `drop()` on it. |
||||
|
||||
|
||||
## License |
||||
|
||||
This micro-library is free software, and licensed under the GNU |
||||
General Public License, version 3.0 or (at your choice), any later |
||||
version. |
||||
|
||||
**Additional Permissions:** For Submission to the Apple App Store: |
||||
Provided that you are otherwise in compliance with the GPLv3 for each |
||||
covered work you convey (including without limitation making the |
||||
Corresponding Source available in compliance with Section 6 of the |
||||
GPLv3), the qaul.net developers also grant you the additional |
||||
permission to convey through the Apple App Store non-source executable |
||||
versions of the Program as incorporated into each applicable covered |
||||
work as Executable Versions only under the Mozilla Public License |
||||
version 2.0. |
||||
|
||||
A copy of both the GPL-3.0 and MPL-2.0 license texts are included in |
||||
this repository. |
||||
|
@ -0,0 +1,8 @@ |
||||
with import <nixpkgs> {}; |
||||
|
||||
stdenv.mkDerivation { |
||||
name = "atomicptr"; |
||||
buildInputs = with pkgs; [ |
||||
rustracer rustup clangStdenv |
||||
]; |
||||
} |
@ -0,0 +1,120 @@ |
||||
#![feature(external_doc)] |
||||
#![doc(include = "../README.md")] |
||||
|
||||
use std::sync::{ |
||||
atomic::{AtomicPtr, Ordering}, |
||||
Arc, |
||||
}; |
||||
use std::{cmp::PartialEq, ops::Deref}; |
||||
|
||||
/// An alias for a referenced pointer
|
||||
pub struct Ref<T> { |
||||
inner: Box<Arc<T>>, |
||||
} |
||||
|
||||
impl<T> Deref for Ref<T> { |
||||
type Target = Arc<T>; |
||||
|
||||
fn deref(&self) -> &Self::Target { |
||||
&self.inner |
||||
} |
||||
} |
||||
|
||||
/// A safe atomic pointer wrapper
|
||||
#[derive(Clone, Debug)] |
||||
pub struct AtomPtr<T> { |
||||
inner: Arc<AtomicPtr<Arc<T>>>, |
||||
} |
||||
|
||||
// Implement Default for all T that implement default
|
||||
impl<T: Default> Default for AtomPtr<T> { |
||||
fn default() -> Self { |
||||
Self::new(T::default()) |
||||
} |
||||
} |
||||
|
||||
impl<T> PartialEq for AtomPtr<T> { |
||||
fn eq(&self, other: &Self) -> bool { |
||||
Arc::ptr_eq(&self.get_ref().inner, &other.get_ref().inner) |
||||
} |
||||
} |
||||
|
||||
impl<T> AtomPtr<T> { |
||||
fn make_raw_ptr(t: T) -> *mut Arc<T> { |
||||
Box::into_raw(Box::new(Arc::new(t))) |
||||
} |
||||
|
||||
/// Create a new atomic pointer for a type
|
||||
pub fn new(t: T) -> Self { |
||||
let ptr = Self::make_raw_ptr(t); |
||||
let inner = Arc::new(AtomicPtr::from(ptr)); |
||||
Self { inner } |
||||
} |
||||
|
||||
/// Get an immutable reference to the current value
|
||||
pub fn get_ref(&self) -> Ref<T> { |
||||
let ptr = self.inner.load(Ordering::Relaxed); |
||||
let b = unsafe { Box::from_raw(ptr) }; |
||||
|
||||
let arc = Arc::clone(&*b); |
||||
std::mem::forget(b); |
||||
|
||||
Ref { |
||||
inner: Box::new(arc), |
||||
} |
||||
} |
||||
|
||||
/// Swap the data entry with a new value, returning the old
|
||||
pub fn swap(&self, new: T) -> Ref<T> { |
||||
let new = Self::make_raw_ptr(new); |
||||
let prev = self.inner.swap(new, Ordering::Relaxed); |
||||
|
||||
let b = unsafe { Box::from_raw(prev) }; |
||||
let arc = Arc::clone(&*b); |
||||
std::mem::forget(b); |
||||
|
||||
Ref { |
||||
inner: Box::new(arc), |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
#[derive(Clone, Debug, PartialEq)] |
||||
struct TestStruct { |
||||
name: String, |
||||
} |
||||
|
||||
#[test] |
||||
fn cloned() { |
||||
let ts = TestStruct { |
||||
name: "Hello".into(), |
||||
}; |
||||
|
||||
let ptr1 = AtomPtr::new(ts); |
||||
let ptr2 = ptr1.clone(); |
||||
|
||||
assert_eq!(ptr1, ptr2); |
||||
} |
||||
|
||||
#[test] |
||||
fn swap() { |
||||
let ts1 = TestStruct { |
||||
name: "Hello 1".into(), |
||||
}; |
||||
|
||||
let ts2 = TestStruct { |
||||
name: "Hello 2".into(), |
||||
}; |
||||
|
||||
// Make an AtomPtr with some data
|
||||
let ptr = AtomPtr::new(ts1.clone()); |
||||
assert_eq!(ptr.get_ref().name, "Hello 1".to_string()); |
||||
|
||||
// Swap the data
|
||||
let still_ts1 = ptr.swap(ts2); |
||||
assert_eq!(ptr.get_ref().name, "Hello 2".to_string()); |
||||
|
||||
// But the old ref is still valid
|
||||
assert_eq!(ts1, *still_ts1.as_ref()); |
||||
} |
Loading…
Reference in new issue