class PEROBS::LockFile
This class implements a file based lock. It can only be taken by one process at a time. It support configurable lock lifetime, maximum retries and pause between retries.
Public Class Methods
new(file_name, options = {})
click to toggle source
Create a new lock for the given file. @param file_name [String] file name of the lock file @param options [Hash] See case statement
# File lib/perobs/LockFile.rb, line 40 def initialize(file_name, options = {}) @file_name = file_name # The handle of the lock file @file = nil # The maximum duration after which a lock file is considered a left-over # from a dead or malefunctioning process. @timeout_secs = 60 * 60 # The maximum number of times we try to get the lock. @max_retries = 5 # The time we wait between retries @pause_secs = 1 options.each do |name, value| case name when :timeout_secs @timeout_secs = value when :max_retries @max_retries = value when :pause_secs @pause_secs = value else PEROBS.log.fatal "Unknown option #{name}" end end end
Public Instance Methods
forced_unlock()
click to toggle source
Erase the lock file. It's essentially a forced unlock method.
# File lib/perobs/LockFile.rb, line 149 def forced_unlock @file = nil if File.exist?(@file_name) begin File.delete(@file_name) PEROBS.log.debug "Lock file #{@file_name} has been deleted." rescue IOError => e PEROBS.log.error "Cannot delete lock file #{@file_name}: " + e.message end end end
is_locked?()
click to toggle source
Check if the lock has been taken. @return [Boolean] true if taken, false otherweise.
# File lib/perobs/LockFile.rb, line 121 def is_locked? File.exist?(@file_name) end
lock()
click to toggle source
Attempt to take the lock. @return [Boolean] true if lock was taken, false otherwise
# File lib/perobs/LockFile.rb, line 68 def lock retries = @max_retries while retries > 0 begin @file = File.open(@file_name, File::RDWR | File::CREAT, 0644) @file.sync = true if @file.flock(File::LOCK_EX | File::LOCK_NB) # We have taken the lock. Write the PID into the file and leave it # open. @file.write($$) @file.flush @file.fsync @file.truncate(@file.pos) PEROBS.log.debug "Lock file #{@file_name} has been taken for " + "process #{$$}" return true else # We did not manage to take the lock file. if @file.mtime <= Time.now - @timeout_secs pid = @file.read.to_i PEROBS.log.info "Old lock file found for PID #{pid}. " + "Removing lock." if is_running?(pid) send_signal('TERM', pid) # Give the process 3 seconds to terminate gracefully. sleep 3 # Then send a SIGKILL to ensure it's gone. send_signal('KILL', pid) if is_running?(pid) end @file.close File.delete(@file_name) if File.exist?(@file_name) else PEROBS.log.debug "Lock file #{@file_name} is taken. Trying " + "to get it #{retries} more times." end end rescue => e PEROBS.log.error "Cannot take lock file #{@file_name}: #{e.message}" return false end retries -= 1 sleep(@pause_secs) end PEROBS.log.info "Failed to get lock file #{@file_name} due to timeout" false end
unlock()
click to toggle source
Release the lock again.
# File lib/perobs/LockFile.rb, line 126 def unlock unless @file PEROBS.log.error "There is no current lock to release" return false end begin @file.flock(File::LOCK_UN) @file.fsync @file.close forced_unlock PEROBS.log.debug "Lock file #{@file_name} for PID #{$$} has been " + "released" rescue => e PEROBS.log.error "Releasing of lock file #{@file_name} failed: " + e.message return false end true end
Private Instance Methods
is_running?(pid)
click to toggle source
# File lib/perobs/LockFile.rb, line 172 def is_running?(pid) begin Process.getpgid(pid) true rescue Errno::ESRCH false end end
send_signal(name, pid)
click to toggle source
# File lib/perobs/LockFile.rb, line 164 def send_signal(name, pid) begin Process.kill(name, pid) rescue => e PEROBS.log.info "Process kill error: #{e.message}" end end