aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-11-05 15:59:38 +0100
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:41 +0100
commit4c4d16d58c3d594938aa3ded26aa7e2fcebfc829 (patch)
treebf2d8039fc5a67b73eae8169e90c2c3f5c00e46e
parent81dbf21e0b48b0d2a964a897d72fbec7c5803399 (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/.envrc1
-rw-r--r--development/libs/atomptr/.gitignore4
-rw-r--r--development/libs/atomptr/Cargo.toml11
l---------development/libs/atomptr/LICENSE1
-rw-r--r--development/libs/atomptr/README.md51
-rw-r--r--development/libs/atomptr/shell.nix8
-rw-r--r--development/libs/atomptr/src/lib.rs120
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());
+}