diff options
author | Mx Kookie <kookie@spacekookie.de> | 2020-11-05 15:59:38 +0100 |
---|---|---|
committer | Mx Kookie <kookie@spacekookie.de> | 2020-12-21 05:19:41 +0100 |
commit | 4c4d16d58c3d594938aa3ded26aa7e2fcebfc829 (patch) | |
tree | bf2d8039fc5a67b73eae8169e90c2c3f5c00e46e | |
parent | 81dbf21e0b48b0d2a964a897d72fbec7c5803399 (diff) |
atomptr: init library with source from netmod-tcp
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.
-rw-r--r-- | development/libs/atomptr/.envrc | 1 | ||||
-rw-r--r-- | development/libs/atomptr/.gitignore | 4 | ||||
-rw-r--r-- | development/libs/atomptr/Cargo.toml | 11 | ||||
l--------- | development/libs/atomptr/LICENSE | 1 | ||||
-rw-r--r-- | development/libs/atomptr/README.md | 51 | ||||
-rw-r--r-- | development/libs/atomptr/shell.nix | 8 | ||||
-rw-r--r-- | development/libs/atomptr/src/lib.rs | 120 |
7 files changed, 196 insertions, 0 deletions
diff --git a/development/libs/atomptr/.envrc b/development/libs/atomptr/.envrc new file mode 100644 index 000000000000..051d09d292a8 --- /dev/null +++ b/development/libs/atomptr/.envrc @@ -0,0 +1 @@ +eval "$(lorri direnv)" diff --git a/development/libs/atomptr/.gitignore b/development/libs/atomptr/.gitignore new file mode 100644 index 000000000000..143b1ca01474 --- /dev/null +++ b/development/libs/atomptr/.gitignore @@ -0,0 +1,4 @@ + +/target/ +**/*.rs.bk +Cargo.lock diff --git a/development/libs/atomptr/Cargo.toml b/development/libs/atomptr/Cargo.toml new file mode 100644 index 000000000000..a1561ab3c8ad --- /dev/null +++ b/development/libs/atomptr/Cargo.toml @@ -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"
\ No newline at end of file diff --git a/development/libs/atomptr/LICENSE b/development/libs/atomptr/LICENSE new file mode 120000 index 000000000000..f837ab4da5f2 --- /dev/null +++ b/development/libs/atomptr/LICENSE @@ -0,0 +1 @@ +../../../licenses/GPL-3.0
\ No newline at end of file diff --git a/development/libs/atomptr/README.md b/development/libs/atomptr/README.md new file mode 100644 index 000000000000..8bbd11ff4502 --- /dev/null +++ b/development/libs/atomptr/README.md @@ -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. + diff --git a/development/libs/atomptr/shell.nix b/development/libs/atomptr/shell.nix new file mode 100644 index 000000000000..3a13912055f1 --- /dev/null +++ b/development/libs/atomptr/shell.nix @@ -0,0 +1,8 @@ +with import <nixpkgs> {}; + +stdenv.mkDerivation { + name = "atomicptr"; + buildInputs = with pkgs; [ + rustracer rustup clangStdenv + ]; +} diff --git a/development/libs/atomptr/src/lib.rs b/development/libs/atomptr/src/lib.rs new file mode 100644 index 000000000000..92410fc4bc38 --- /dev/null +++ b/development/libs/atomptr/src/lib.rs @@ -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()); +} |