diff --git a/Cargo.toml b/Cargo.toml index 3e75c37..e9015d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,9 @@ keywords = ["process", "inter-process", "cross-process", "flock", "CreateMutexW" repository = "https://github.com/oblique/named-lock" [dependencies] -thiserror = "1.0.35" once_cell = "1.14.0" -parking_lot = "0.12.1" +parking_lot = { version = "0.12.1", features = ["arc_lock", "send_guard"] } +thiserror = "1.0.35" [target.'cfg(unix)'.dependencies] libc = "0.2.132" @@ -24,6 +24,7 @@ winapi = { version = "0.3.9", features = ["handleapi", "synchapi", "winbase", "w widestring = "1.0.2" [dev-dependencies] +static_assertions = "1.1.0" uuid = { version = "1.1.2", features = ["v4"] } [package.metadata.docs.rs] diff --git a/src/lib.rs b/src/lib.rs index 15e5032..de22b77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,8 +20,10 @@ //! ``` use once_cell::sync::Lazy; -use parking_lot::{Mutex, MutexGuard}; +use parking_lot::lock_api::ArcMutexGuard; +use parking_lot::{Mutex, RawMutex}; use std::collections::HashMap; +use std::fmt; #[cfg(unix)] use std::path::{Path, PathBuf}; use std::sync::{Arc, Weak}; @@ -152,7 +154,7 @@ impl NamedLock { /// /// If it is already locked, `Error::WouldBlock` will be returned. pub fn try_lock(&self) -> Result { - let guard = self.raw.try_lock().ok_or(Error::WouldBlock)?; + let guard = self.raw.try_lock_arc().ok_or(Error::WouldBlock)?; guard.try_lock()?; @@ -163,7 +165,7 @@ impl NamedLock { /// Lock named lock. pub fn lock(&self) -> Result { - let guard = self.raw.lock(); + let guard = self.raw.lock_arc(); guard.lock()?; @@ -174,21 +176,28 @@ impl NamedLock { } /// Scoped guard that unlocks NamedLock. -#[derive(Debug)] -pub struct NamedLockGuard<'r> { - raw: MutexGuard<'r, RawNamedLock>, +pub struct NamedLockGuard { + raw: ArcMutexGuard, } -impl<'r> Drop for NamedLockGuard<'r> { +impl Drop for NamedLockGuard { fn drop(&mut self) { let _ = self.raw.unlock(); } } +impl fmt::Debug for NamedLockGuard { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NamedLockGuard").field("raw", &*self.raw).finish() + } +} + #[cfg(test)] mod tests { use super::*; + use static_assertions::assert_impl_all; use std::env; + use std::fmt::Debug; use std::process::{Child, Command}; use std::thread::sleep; use std::time::Duration; @@ -272,6 +281,28 @@ mod tests { Ok(()) } + #[test] + fn owned_guard() -> Result<()> { + let uuid = Uuid::new_v4().as_hyphenated().to_string(); + let lock1 = NamedLock::create(&uuid)?; + let lock2 = NamedLock::create(&uuid)?; + + // Lock + let guard1 = lock1.try_lock()?; + assert!(matches!(lock2.try_lock(), Err(Error::WouldBlock))); + + // Dropping `lock1` should not affect the state of the lock. + // If `guard1` is not dropped the lock must stay locked. + drop(lock1); + assert!(matches!(lock2.try_lock(), Err(Error::WouldBlock))); + + // Unlock by dropping the `guard1` + drop(guard1); + let _guard2 = lock2.try_lock()?; + + Ok(()) + } + #[test] fn invalid_names() { assert!(matches!(NamedLock::create(""), Err(Error::EmptyName))); @@ -291,4 +322,10 @@ mod tests { Err(Error::InvalidCharacter) )); } + + #[test] + fn check_traits() { + assert_impl_all!(NamedLock: Debug, Send, Sync); + assert_impl_all!(NamedLockGuard: Debug, Send, Sync); + } } diff --git a/src/unix.rs b/src/unix.rs index 58b90ce..ee99951 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -16,8 +16,8 @@ impl RawNamedLock { let lock_file = OpenOptions::new() .write(true) .create_new(true) - .open(&lock_path) - .or_else(|_| OpenOptions::new().write(true).open(&lock_path)) + .open(lock_path) + .or_else(|_| OpenOptions::new().write(true).open(lock_path)) .map_err(Error::CreateFailed)?; Ok(RawNamedLock {