philiprehberger-lock_kit
File-based and PID locking for process coordination with stale lock detection, read-write locks, and lock owner identification.
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem 'philiprehberger-lock_kit'Or install directly:
gem install philiprehberger-lock_kitUsage
require 'philiprehberger/lock_kit'
Philiprehberger::LockKit.with_file_lock('/tmp/my.lock') do
# exclusive work here
endFile Locking with Timeout
Philiprehberger::LockKit.with_file_lock('/tmp/my.lock', timeout: 5) do
# waits up to 5 seconds for the lock
endPID File Locking
Philiprehberger::LockKit.with_pid_lock('my_worker') do
# only one process with this name can run at a time
endRead-Write Locks
# Multiple readers can hold the lock concurrently
Philiprehberger::LockKit.with_read_lock('/tmp/data.lock', timeout: 5) do
# read shared data
end
# Write lock is exclusive — no readers or other writers allowed
Philiprehberger::LockKit.with_write_lock('/tmp/data.lock', timeout: 5) do
# write shared data
endAutomatic Stale Lock Cleanup
Philiprehberger::LockKit.with_file_lock('/tmp/my.lock', auto_cleanup: true) do
# automatically removes stale locks from dead processes
end
Philiprehberger::LockKit.with_pid_lock('my_worker', auto_cleanup: true) do
# same for PID locks
endLock Owner Identification
info = Philiprehberger::LockKit.owner('/tmp/my.lock')
# => { pid: 12345, hostname: 'web-01', acquired_at: 2026-03-28 12:00:00 +0000 }Lock Waiting with Callbacks
Philiprehberger::LockKit.with_file_lock('/tmp/my.lock', timeout: 10, on_wait: ->(elapsed) { puts "Waiting #{elapsed}s..." }) do
# callback fires every 0.5s while waiting
endForce Break Lock
# Break stale locks only (raises if held by a live process)
Philiprehberger::LockKit.break!('/tmp/my.lock')
# => { broken: true, previous_owner: { pid: 12345, hostname: 'web-01', acquired_at: ... } }
# Force break any lock regardless of status
Philiprehberger::LockKit.break!('/tmp/my.lock', force: true)Manual File Lock
lock = Philiprehberger::LockKit::FileLock.new('/tmp/my.lock')
lock.acquire(timeout: 10)
# ... do work ...
lock.releaseManual PID Lock
lock = Philiprehberger::LockKit::PidLock.new('my_worker', dir: '/var/run')
lock.acquire
# ... do work ...
lock.releaseChecking Lock Status
Philiprehberger::LockKit.locked?('/tmp/my.lock') # => true/false
Philiprehberger::LockKit.stale?('/tmp/my_worker.pid') # => true/falseAPI
LockKit
| Method | Description |
|---|---|
.with_file_lock(path, timeout: nil, auto_cleanup: false, on_wait: nil) { } |
Execute block with exclusive file lock |
.with_pid_lock(name, dir: Dir.tmpdir, auto_cleanup: false) { } |
Execute block with PID file lock |
.with_read_lock(path, timeout: nil) { } |
Execute block with shared read lock |
.with_write_lock(path, timeout: nil) { } |
Execute block with exclusive write lock |
.locked?(path) |
Check if a file is currently locked |
.stale?(pid_file) |
Check if a PID file references a dead process |
.owner(path) |
Get lock owner metadata (pid, hostname, acquired_at) |
.break!(path, force: false) |
Break a lock (stale only by default, any with force) |
LockKit::FileLock
| Method | Description |
|---|---|
.new(path) |
Create a file lock instance |
#acquire(timeout: nil, auto_cleanup: false, on_wait: nil) |
Acquire exclusive lock with optional timeout, cleanup, and wait callback |
#release |
Release the lock and close the file handle |
#locked? |
Check if the file is currently locked |
#owner |
Get lock owner metadata |
LockKit::PidLock
| Method | Description |
|---|---|
.new(name, dir: Dir.tmpdir) |
Create a PID lock instance |
#acquire(auto_cleanup: false) |
Acquire PID lock, raises if held by a living process |
#release |
Release lock and remove PID file |
#locked? |
Check if lock is held by a living process |
#stale? |
Check if PID file references a dead process |
#owner |
Get lock owner metadata |
LockKit::ReadWriteLock
| Method | Description |
|---|---|
.new(path) |
Create a read-write lock instance |
#acquire_read(timeout: nil) |
Acquire shared read lock |
#release_read |
Release the read lock |
#acquire_write(timeout: nil) |
Acquire exclusive write lock |
#release_write |
Release the write lock |
#reader_count |
Get current number of active readers |
Development
bundle install
bundle exec rspec
bundle exec rubocopSupport
If you find this project useful: