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.
wip/yesman
Katharina Fey 4 years ago
parent 81dbf21e0b
commit 4c4d16d58c
  1. 1
      development/libs/atomptr/.envrc
  2. 4
      development/libs/atomptr/.gitignore
  3. 11
      development/libs/atomptr/Cargo.toml
  4. 1
      development/libs/atomptr/LICENSE
  5. 51
      development/libs/atomptr/README.md
  6. 8
      development/libs/atomptr/shell.nix
  7. 120
      development/libs/atomptr/src/lib.rs

@ -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…
Cancel
Save