Pidlock

Struct Pidlock 

Source
pub struct Pidlock { /* private fields */ }
Expand description

A pid-centered lock. A lock is considered “acquired” when a file exists on disk at the path specified, containing the process id of the locking process.

§Examples

§Basic Usage

use pidlock::Pidlock;
use std::fs;

// Create a lock in a temporary location
let temp_dir = std::env::temp_dir();
let lock_path = temp_dir.join("example.pid");

let mut lock = Pidlock::new_validated(&lock_path)?;

// Acquire the lock
lock.acquire()?;
assert!(lock.locked());

// The lock file now exists and contains our PID
assert!(lock.exists());

// Release the lock
lock.release()?;
assert!(!lock.locked());
assert!(!lock.exists()); // File is removed

§Handling Lock Conflicts

use pidlock::{Pidlock, PidlockError};
use std::fs;

let temp_dir = std::env::temp_dir();
let lock_path = temp_dir.join("conflict_example.pid");

// First lock
let mut lock1 = Pidlock::new_validated(&lock_path)?;
lock1.acquire()?;

// Try to acquire the same lock from another instance
let mut lock2 = Pidlock::new_validated(&lock_path)?;
match lock2.acquire() {
    Err(PidlockError::LockExists) => {
        println!("Lock is already held by another process");
        // This is expected behavior
    }
    _ => panic!("Should have failed with LockExists"),
}

// Clean up
lock1.release()?;

§Automatic Cleanup on Drop

use pidlock::Pidlock;
use std::fs;

let temp_dir = std::env::temp_dir();
let lock_path = temp_dir.join("drop_example.pid");

{
    let mut lock = Pidlock::new_validated(&lock_path)?;
    lock.acquire()?;
    assert!(lock.exists());
    // Lock goes out of scope here and is automatically cleaned up
}

// Lock file should be removed by the Drop implementation
assert!(!lock_path.exists());

Implementations§

Source§

impl Pidlock

Source

pub fn new(path: impl AsRef<Path>) -> Self

👎Deprecated since 0.2.0: Use new_validated for path validation and better cross-platform compatibility

Create a new Pidlock at the provided path.

For backwards compatibility, this method does not validate the path. Use new_validated if you want path validation.

Source

pub fn new_validated(path: impl AsRef<Path>) -> Result<Self, PidlockError>

Create a new Pidlock at the provided path with path validation.

This is the recommended way to create a Pidlock as it validates the path for cross-platform compatibility and common issues.

§Arguments
  • path - The path where the lock file will be created. The parent directory must exist or be creatable.
§Returns
  • Ok(Pidlock) - A new pidlock instance ready to be acquired
  • Err(PidlockError::InvalidPath) - If the path is not suitable for use as a lock file
§Examples
use pidlock::Pidlock;
use std::env;

// Valid path
let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("valid.pid");
let lock = Pidlock::new_validated(&lock_path)?;
use pidlock::{Pidlock, PidlockError, InvalidPathError};

// Invalid path with problematic characters
let result = Pidlock::new_validated("invalid<file.pid");
match result {
    Err(PidlockError::InvalidPath(InvalidPathError::ProblematicCharacter { .. })) => {
        // Expected error for invalid characters
    }
    _ => panic!("Should have failed with InvalidPath"),
}
§Errors

Returns PidlockError::InvalidPath if the path is not suitable for use as a lock file.

Source

pub fn acquire(&mut self) -> Result<(), PidlockError>

Acquire a lock.

This method attempts to create the lock file containing the current process ID. If a stale lock file exists (from a dead process), it will be automatically cleaned up.

§Returns
  • Ok(()) - Lock was successfully acquired
  • Err(PidlockError::LockExists) - Another active process holds the lock
  • Err(PidlockError::InvalidState) - Lock is already acquired or released
  • Err(PidlockError::IOError) - File system error occurred
§Examples
use pidlock::{Pidlock, PidlockError};
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("acquire_example.pid");

let mut lock = Pidlock::new_validated(&lock_path)?;

match lock.acquire() {
    Ok(()) => {
        println!("Lock acquired successfully!");
        // Do your work here...
        lock.release()?;
    }
    Err(PidlockError::LockExists) => {
        println!("Another instance is already running");
    }
    Err(e) => {
        eprintln!("Unexpected error: {}", e);
    }
}
§Behavior
  1. State validation: Can only be called on a New pidlock
  2. Stale lock cleanup: Automatically removes locks from dead processes
  3. Atomic creation: Uses O_EXCL/CREATE_NEW for atomic lock file creation
  4. Secure permissions: Creates files with restrictive permissions (600 on Unix)
  5. Reliable writes: Flushes data to disk before returning success
Source

pub fn locked(&self) -> bool

Returns true when the lock is in an acquired state.

This is a local state check only - it tells you whether this Pidlock instance has successfully acquired a lock, but doesn’t check if the lock file still exists on disk or if another process might have interfered with it.

§Returns

true if this Pidlock instance has acquired the lock, false otherwise.

§Examples
use pidlock::Pidlock;
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("locked_example.pid");

let mut lock = Pidlock::new_validated(&lock_path)?;

// Initially not locked
assert!(!lock.locked());

// After acquiring
lock.acquire()?;
assert!(lock.locked());

// After releasing
lock.release()?;
assert!(!lock.locked());
§Note

For checking if a lock file exists on disk, use exists(). For checking if a lock is held by an active process, use is_active().

Source

pub fn exists(&self) -> bool

Check if the lock file exists on disk. This is a read-only operation that doesn’t modify the lock state.

§Returns

true if the lock file exists, false otherwise.

§Examples
use pidlock::Pidlock;
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("exists_example.pid");

let lock = Pidlock::new_validated(&lock_path)?;

// Initially, no lock file exists
assert!(!lock.exists());

// Create a lock file manually to test
std::fs::write(&lock_path, "12345")?;
assert!(lock.exists());

// Clean up
std::fs::remove_file(&lock_path)?;
Source

pub fn is_active(&self) -> Result<bool, PidlockError>

Check if the lock file exists and if so, whether it’s stale (owned by a dead process). This is a read-only operation that doesn’t modify the lock state.

§Returns

Ok(true) if a lock exists and the owning process is still running, Ok(false) if no lock exists or the lock is stale, Err(_) if there was an error determining the lock status.

§Examples
use pidlock::Pidlock;
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("is_active_example.pid");

let lock = Pidlock::new_validated(&lock_path)?;

match lock.is_active()? {
    true => println!("Lock is held by an active process"),
    false => println!("No active lock found"),
}

// Test with our own process
std::fs::write(&lock_path, std::process::id().to_string())?;
assert!(lock.is_active()?); // Our process is definitely active

// Clean up
std::fs::remove_file(&lock_path)?;
Source

pub fn release(&mut self) -> Result<(), PidlockError>

Release the lock.

This method removes the lock file from disk and transitions the lock to the Released state. Once released, the lock cannot be re-acquired (create a new Pidlock instance instead).

§Returns
  • Ok(()) - Lock was successfully released
  • Err(PidlockError::InvalidState) - Lock is not currently acquired
  • Err(PidlockError::IOError) - File system error occurred during removal
§Examples
use pidlock::Pidlock;
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("release_example.pid");

let mut lock = Pidlock::new_validated(&lock_path)?;
lock.acquire()?;

// Explicitly release the lock
lock.release()?;

// Lock file should be gone
assert!(!lock.exists());
assert!(!lock.locked());
§Note

Releasing a lock is optional - the Drop implementation will automatically clean up acquired locks when the Pidlock goes out of scope. However, explicit release is recommended for better error handling and immediate cleanup.

Source

pub fn get_owner(&self) -> Result<Option<i32>, PidlockError>

Gets the owner of this lockfile, returning the PID if it exists and is valid.

This method reads the lock file and attempts to parse the PID contained within. If the PID is invalid, the process no longer exists, or the file is corrupted, the lock file will be automatically cleaned up.

§Returns
  • Ok(Some(pid)) - Lock file exists and contains a valid PID for an active process
  • Ok(None) - No lock file exists, or the lock file was invalid and cleaned up
  • Err(_) - I/O error occurred while reading or cleaning up the file
§Examples
use pidlock::Pidlock;
use std::env;

let temp_dir = env::temp_dir();
let lock_path = temp_dir.join("owner_example.pid");

let mut lock = Pidlock::new_validated(&lock_path)?;

// No owner initially
assert_eq!(lock.get_owner()?, None);

// After acquiring, we should be the owner
lock.acquire()?;
if let Some(owner_pid) = lock.get_owner()? {
    println!("Lock is owned by PID: {}", owner_pid);
    assert_eq!(owner_pid, std::process::id() as i32);
}

lock.release()?;
§Behavior

This method will automatically clean up lock files in the following cases:

  • File contains non-numeric content
  • File contains a PID that doesn’t correspond to a running process
  • File contains a PID outside the valid range for the platform
  • File is empty or contains only whitespace

Trait Implementations§

Source§

impl Debug for Pidlock

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Pidlock

Source§

fn drop(&mut self)

Automatically release the lock when the Pidlock goes out of scope. This ensures that lock files are cleaned up even if the process panics or exits unexpectedly while holding a lock.

Note: This implementation uses a best-effort approach. If cleanup fails, we don’t panic because that could mask the original panic that triggered the drop. Errors are logged when the log feature is enabled.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.