/usr/lib/ruby/vendor_ruby/em-hiredis/lock.rb is in ruby-em-hiredis 0.3.0-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | require 'securerandom'
module EM::Hiredis
# Cross-process re-entrant lock, backed by redis
class Lock
EM::Hiredis::Client.load_scripts_from(File.expand_path("../lock_lua", __FILE__))
# Register a callback which will be called 1s before the lock expires
# This is an informational callback, there is no hard guarantee on the timing
# of its invocation because the callback firing and lock key expiry are handled
# by different clocks (the client process and redis server respectively)
def onexpire(&blk); @onexpire = blk; end
def initialize(redis, key, timeout)
unless timeout.kind_of?(Fixnum) && timeout >= 1
raise "Timeout must be an integer and >= 1s"
end
@redis, @key, @timeout = redis, key, timeout
@token = SecureRandom.hex
end
# Acquire the lock
#
# This is a re-entrant lock, re-acquiring will succeed and extend the timeout
#
# Returns a deferrable which either succeeds if the lock can be acquired, or fails if it cannot.
def acquire
df = EM::DefaultDeferrable.new
@redis.lock_acquire([@key], [@token, @timeout]).callback { |success|
if (success)
EM::Hiredis.logger.debug "#{to_s} acquired"
EM.cancel_timer(@expire_timer) if @expire_timer
@expire_timer = EM.add_timer(@timeout - 1) {
EM::Hiredis.logger.debug "#{to_s} Expires in 1s"
@onexpire.call if @onexpire
}
df.succeed
else
EM::Hiredis.logger.debug "#{to_s} failed to acquire"
df.fail("Lock is not available")
end
}.errback { |e|
EM::Hiredis.logger.error "#{to_s} Error acquiring lock #{e}"
df.fail(e)
}
df
end
# Release the lock
#
# Returns a deferrable
def unlock
EM.cancel_timer(@expire_timer) if @expire_timer
df = EM::DefaultDeferrable.new
@redis.lock_release([@key], [@token]).callback { |keys_removed|
if keys_removed > 0
EM::Hiredis.logger.debug "#{to_s} released"
df.succeed
else
EM::Hiredis.logger.debug "#{to_s} could not release, not held"
df.fail("Cannot release a lock we do not hold")
end
}.errback { |e|
EM::Hiredis.logger.error "#{to_s} Error releasing lock #{e}"
df.fail(e)
}
df
end
# This should not be used in normal operation.
# Force clear without regard to who owns the lock.
def clear
EM::Hiredis.logger.warn "#{to_s} Force clearing lock (unsafe)"
EM.cancel_timer(@expire_timer) if @expire_timer
@redis.del(@key)
end
def to_s
"[lock #{@key}]"
end
end
end
|