summaryrefslogtreecommitdiff
path: root/src/collection.rs
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2024-03-09 16:53:12 -0500
committerMica White <botahamec@outlook.com>2024-03-09 16:53:12 -0500
commit6a54884b292987fc1371bf062c42e964b6a4b0fe (patch)
tree7b4c20c4150522b0d6d8e3f5db8fbe17eb80abc5 /src/collection.rs
parent8ea16a606bfcc1ba535f6cef3cb4c162f91d2eb0 (diff)
Pointer checks
Diffstat (limited to 'src/collection.rs')
-rw-r--r--src/collection.rs104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/collection.rs b/src/collection.rs
new file mode 100644
index 0000000..809f340
--- /dev/null
+++ b/src/collection.rs
@@ -0,0 +1,104 @@
+use std::{
+ marker::PhantomData,
+ ops::{Deref, DerefMut},
+};
+
+use crate::{key::Keyable, lockable::Lockable};
+
+fn contains_duplicates(l: &[usize]) -> bool {
+ for i in 0..l.len() {
+ for j in 0..l.len() {
+ if i != j && l[i] == l[j] {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+pub struct LockCollection<L> {
+ collection: L,
+}
+
+/// A guard for a generic [`Lockable`] type.
+pub struct LockGuard<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable + 'key> {
+ guard: L::Output,
+ key: Key,
+ _phantom: PhantomData<&'key ()>,
+}
+
+impl<L> LockCollection<L> {
+ /// Creates a new collections of locks.
+ ///
+ /// # Safety
+ ///
+ /// This results in undefined behavior if any locks are presented twice
+ /// within this collection.
+ pub const unsafe fn new_unchecked(collection: L) -> Self {
+ Self { collection }
+ }
+}
+
+impl<'a, L: Lockable<'a>> LockCollection<L> {
+ /// Creates a new collection of locks.
+ ///
+ /// This returns `None` if any locks are found twice in the given collection.
+ pub fn new(collection: L) -> Option<Self> {
+ let ptrs = collection.get_ptrs();
+ if contains_duplicates(&ptrs) {
+ return None;
+ }
+
+ Some(Self { collection })
+ }
+
+ /// Locks the lockable type and returns a guard that can be used to access
+ /// the underlying data.
+ pub fn lock<'key: 'a, Key: Keyable + 'key>(&'a self, key: Key) -> LockGuard<'a, 'key, L, Key> {
+ LockGuard {
+ // safety: we have the thread's key
+ guard: unsafe { self.collection.lock() },
+ key,
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Attempts to lock the guard without blocking.
+ ///
+ /// If successful, this method returns a guard that can be used to access
+ /// the data. Otherwise, `None` is returned.
+ pub fn try_lock<'key: 'a, Key: Keyable + 'key>(
+ &'a self,
+ key: Key,
+ ) -> Option<LockGuard<'a, 'key, L, Key>> {
+ // safety: we have the thread's key
+ unsafe { self.collection.try_lock() }.map(|guard| LockGuard {
+ guard,
+ key,
+ _phantom: PhantomData,
+ })
+ }
+
+ /// Unlocks the underlying lockable data type, returning the key that's
+ /// associated with it.
+ #[allow(clippy::missing_const_for_fn)]
+ pub fn unlock<'key: 'a, Key: Keyable + 'key>(guard: LockGuard<'a, 'key, L, Key>) -> Key {
+ drop(guard.guard);
+ guard.key
+ }
+}
+
+impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> Deref for LockGuard<'a, 'key, L, Key> {
+ type Target = L::Output;
+
+ fn deref(&self) -> &Self::Target {
+ &self.guard
+ }
+}
+
+impl<'a, 'key: 'a, L: Lockable<'a>, Key: Keyable> DerefMut for LockGuard<'a, 'key, L, Key> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.guard
+ }
+}