Class: Philiprehberger::LockKit::PidLock
- Inherits:
-
Object
- Object
- Philiprehberger::LockKit::PidLock
- Defined in:
- lib/philiprehberger/lock_kit/pid_lock.rb
Overview
PID-file based lock with stale process detection
Writes the current process ID, hostname, and timestamp to a file in JSON format. Other processes can check the file to determine if the lock holder is still alive, enabling automatic recovery from crashed processes.
Instance Method Summary collapse
-
#acquire(auto_cleanup: false, ttl: nil) ⇒ true
Acquire the PID lock.
-
#expired? ⇒ Boolean
Check whether the lock has expired based on its TTL.
-
#initialize(name, dir: Dir.tmpdir) ⇒ PidLock
constructor
A new instance of PidLock.
-
#locked? ⇒ Boolean
Check whether the lock is currently held by a living process.
-
#owner ⇒ Hash?
Read lock owner metadata from the PID file.
-
#release ⇒ void
Release the lock by removing the PID file.
-
#stale? ⇒ Boolean
Check whether the PID file references a dead process.
Constructor Details
#initialize(name, dir: Dir.tmpdir) ⇒ PidLock
Returns a new instance of PidLock.
17 18 19 20 21 22 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 17 def initialize(name, dir: Dir.tmpdir) @name = name @dir = dir @pid_path = File.join(dir, "#{name}.pid") @acquired = false end |
Instance Method Details
#acquire(auto_cleanup: false, ttl: nil) ⇒ true
Acquire the PID lock
Creates a PID file containing metadata (PID, hostname, timestamp) in JSON format. If a PID file already exists, checks whether the owning process is still alive. Stale PID files from dead processes are automatically cleaned up.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 35 def acquire(auto_cleanup: false, ttl: nil) @ttl = ttl if File.exist?(@pid_path) # Check TTL expiration first if lock_expired? FileUtils.rm_f(@pid_path) else existing_pid = read_pid if existing_pid && process_alive?(existing_pid) raise Error, "Lock '#{@name}' is held by process #{existing_pid}" end # Stale PID file — remove it FileUtils.rm_f(@pid_path) end end = { 'pid' => Process.pid, 'hostname' => Socket.gethostname, 'acquired_at' => Time.now.iso8601 } ['expires_at'] = (Time.now + ttl).iso8601 if ttl File.write(@pid_path, JSON.generate()) @acquired = true true end |
#expired? ⇒ Boolean
Check whether the lock has expired based on its TTL
108 109 110 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 108 def expired? lock_expired? end |
#locked? ⇒ Boolean
Check whether the lock is currently held by a living process
Returns false if the lock has expired (TTL elapsed).
83 84 85 86 87 88 89 90 91 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 83 def locked? return false unless File.exist?(@pid_path) return false if expired? pid = read_pid return false unless pid process_alive?(pid) end |
#owner ⇒ Hash?
Read lock owner metadata from the PID file
115 116 117 118 119 120 121 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 115 def owner return nil unless File.exist?(@pid_path) rescue Errno::ENOENT nil end |
#release ⇒ void
This method returns an undefined value.
Release the lock by removing the PID file
Only removes the file if it was written by this process.
70 71 72 73 74 75 76 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 70 def release return unless @acquired File.delete(@pid_path) if File.exist?(@pid_path) && read_pid == Process.pid @acquired = false end |
#stale? ⇒ Boolean
Check whether the PID file references a dead process
96 97 98 99 100 101 102 103 |
# File 'lib/philiprehberger/lock_kit/pid_lock.rb', line 96 def stale? return false unless File.exist?(@pid_path) pid = read_pid return true unless pid !process_alive?(pid) end |