summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2024-03-09 09:29:38 -0500
committerMica White <botahamec@outlook.com>2024-03-09 09:29:38 -0500
commit1d1f12da8c9251668a62217620c94fc732ac9f78 (patch)
tree01ac228ffc45b27f4b1a6d397d8dbcbf5a46c25f
parent1058ce42d330ba3a9d9e2ce6cc50001ef9258bef (diff)
docs
-rw-r--r--Cargo.toml1
-rw-r--r--README.md16
-rw-r--r--src/lockable.rs2
-rw-r--r--src/mutex.rs10
4 files changed, 16 insertions, 13 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 2498427..527b736 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,4 +13,3 @@ once_cell = "1"
lock_api = "0.4"
parking_lot = "0.12"
spin = "0.9"
-libc = "0.2"
diff --git a/README.md b/README.md
index d0c82f8..4ce0af8 100644
--- a/README.md
+++ b/README.md
@@ -70,22 +70,22 @@ The `ThreadKey` is a mostly-zero cost abstraction. It doesn't use any memory, an
The real performance cost comes from the fact that the sets of multiple locks must be atomic. The problem is that this library must iterate through the list of locks, and not complete until every single one of them is unlocked. This also means that attempting to lock multiple mutexes gives you a lower chance of ever running. Only one needs to be locked for the operation to need a reset. This problem can be prevented by not doing that in your code. Resources should be obtained in the same order on every thread.
-Currently, a spinlock is used as the mutex. This should be fixed in future releases.
-
## Future Work
-Are the ergonomics really correct here? This is completely untreaded territory. Maybe there are some useful helper methods we don't have here yet.
+Are the ergonomics here any good? This is completely untreaded territory. Maybe there are some useful helper methods we don't have here yet. Maybe `try_lock` should return a `Result`. Maybe `lock_api` or `spin` implements some useful methods that I kept out for this proof of concept.
There might be some promise in trying to prevent circular wait. There could be a special type that only allows the locking mutexes in a specific order. This would still require a thread key so that nobody tries to unlock multiple lock sequences at the same time. But this could improve performance, since we wouldn't need to worry about making sure the lock operations are atomic.
-It would be nice to try to get this working without the standard library. There are a few problems with this though. For instance, this crate uses `thread_local` to allow other threads to have their own keys. Also, the only practical type of mutex that would work is a spinlock. Although, more could be implemented using the `RawMutex` trait.
+Although this library is able to successfully prevent deadlocks, livelocks may still be an issue. Imagine thread 1 gets resource 1, thread 2 gets resource 2, thread 1 realizes it can't get resource 2, thread 2 realizes it can't get resource 1, thread 1 drops resource 1, thread 2 drops resource 2, and then repeat forever. In practice, this situation probably wouldn't last forever. But it would be nice if this could be prevented somehow.
+
+I want to try to get this working without the standard library. There are a few problems with this though. For instance, this crate uses `thread_local` to allow other threads to have their own keys. Also, the only practical type of mutex that would work is a spinlock. Although, more could be implemented using the `RawMutex` trait.
-Currently, the mutex is implemented using a spinlock. We need to not do that. We could use parking lot, or the standard library.
+It'd be nice to be able to use the mutexes built into the operating system. Using `std::sync::Mutex` sounds promising, but it doesn't implement `RawMutex`, and implementing that is very difficult, if not impossible.
-A more fair system for getting sets locks would help, but I have no clue what that looks like.
+A more fair system for getting sets of locks would help, but I have no clue what that looks like.
-A read-write lock would be very useful here, and maybe other primitives such as condvars and once?
+A read-write lock would be very useful here, and maybe other primitives such as condvars and barriers?
-Personally, I don't like mutex poisoning, but maybe it can be worked into the library if you're into that sort of thing. For now, that can be implemented using the `poison` crate.
+Personally, I don't like mutex poisoning, but maybe it can be worked into the library if you're into that sort of thing.
More types might be lockable using a `LockGuard`. \ No newline at end of file
diff --git a/src/lockable.rs b/src/lockable.rs
index 7ddfec0..1aad8d9 100644
--- a/src/lockable.rs
+++ b/src/lockable.rs
@@ -39,7 +39,7 @@ pub unsafe trait Lockable<'a>: sealed::Sealed {
/// Attempt to lock without blocking.
///
- /// Returns `Ok` if successful, `None` otherwise.
+ /// Returns `Some` if successful, `None` otherwise.
///
/// # Safety
///
diff --git a/src/mutex.rs b/src/mutex.rs
index 5aed0fc..7252dfc 100644
--- a/src/mutex.rs
+++ b/src/mutex.rs
@@ -26,7 +26,7 @@ pub type ParkingMutex<T> = Mutex<T, parking_lot::RawMutex>;
///
/// [`lock`]: `Mutex::lock`
/// [`try_lock`]: `Mutex::try_lock`
-pub struct Mutex<T: ?Sized, R: RawMutex> {
+pub struct Mutex<T: ?Sized, R> {
raw: R,
value: UnsafeCell<T>,
}
@@ -121,7 +121,9 @@ impl<T, R: RawMutex> Mutex<T, R> {
value: UnsafeCell::new(value),
}
}
+}
+impl<T, R> Mutex<T, R> {
/// Consumes this mutex, returning the underlying data.
///
/// # Examples
@@ -137,7 +139,7 @@ impl<T, R: RawMutex> Mutex<T, R> {
}
}
-impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
+impl<T: ?Sized, R> Mutex<T, R> {
/// Returns a mutable reference to the underlying data.
///
/// Since this call borrows `Mutex` mutably, no actual locking is taking
@@ -148,7 +150,7 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
/// ```
/// use happylock::{ThreadKey, Mutex};
///
- /// let key = ThreadKey::lock();
+ /// let key = ThreadKey::lock().unwrap();
/// let mut mutex = Mutex::new(0);
/// *mutex.get_mut() = 10;
/// assert_eq!(*mutex.lock(key), 10);
@@ -156,7 +158,9 @@ impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
+}
+impl<T: ?Sized, R: RawMutex> Mutex<T, R> {
/// Block the thread until this mutex can be locked, and lock it.
///
/// Upon returning, the thread is the only thread with a lock on the