Module: Redis::EM::Mutex::ScriptHandlerMixin

Includes:
Errors
Defined in:
lib/redis/em-mutex/script_handler.rb

Defined Under Namespace

Modules: Scripts

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.can_refresh_expired?Boolean

Returns:

  • (Boolean)


8
# File 'lib/redis/em-mutex/script_handler.rb', line 8

def self.can_refresh_expired?; false end

Instance Method Details

#expiration_timestampObject

Returns timestamp at which the semaphore will expire or have expired. Returns ‘nil` if the semaphore wasn’t locked by current owner.

The check is performed only on the Mutex object instance and should only be used as a hint. For reliable lock status information use #refresh or #owned? instead.



103
104
105
# File 'lib/redis/em-mutex/script_handler.rb', line 103

def expiration_timestamp
  @lock_expire if @lock_expire && owner_ident == @locked_owner_id
end

#expired?Boolean

Returns ‘true` when the semaphore is being held and have already expired. Returns `false` when the semaphore is still locked and valid or `nil` if the semaphore wasn’t locked by current owner.

The check is performed only on the Mutex object instance and should only be used as a hint. For reliable lock status information use #refresh or #owned? instead.

Returns:

  • (Boolean)


74
75
76
# File 'lib/redis/em-mutex/script_handler.rb', line 74

def expired?
  Time.now.to_f > @lock_expire if @lock_expire && owner_ident == @locked_owner_id
end

#expires_atObject

Returns local time at which the semaphore will expire or have expired. Returns ‘nil` if the semaphore wasn’t locked by current owner.

The check is performed only on the Mutex object instance and should only be used as a hint. For reliable lock status information use #refresh or #owned? instead.



94
95
96
# File 'lib/redis/em-mutex/script_handler.rb', line 94

def expires_at
  Time.at(@lock_expire) if @lock_expire && owner_ident == @locked_owner_id
end

#expires_inObject

Returns the number of seconds left until the semaphore expires. The number of seconds less than 0 means that the semaphore expired and could be grabbed by some other owner. Returns ‘nil` if the semaphore wasn’t locked by current owner.

The check is performed only on the Mutex object instance and should only be used as a hint. For reliable lock status information use #refresh or #owned? instead.



85
86
87
# File 'lib/redis/em-mutex/script_handler.rb', line 85

def expires_in
  @lock_expire.to_f - Time.now.to_f if @lock_expire && owner_ident == @locked_owner_id
end

#lock(block_timeout = nil) ⇒ Object

Attempts to grab the lock and waits if it isn’t available. Raises MutexError if mutex was locked by the current owner. Returns ‘true` if lock was successfully obtained. Returns `false` if lock wasn’t available within ‘block_timeout` seconds.

If ‘block_timeout` is `nil` or omited this method uses Mutex#block_timeout. If also Mutex#block_timeout is nil this method returns only after lock has been granted.

Use Mutex#expire_timeout= to set lock expiration timeout. Otherwise global Mutex.default_expire is used.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/redis/em-mutex/script_handler.rb', line 162

def lock(block_timeout = nil)
  block_timeout||= self.block_timeout
  names = @ns_names
  timer = fiber = nil
  try_again = false
  sig_proc = proc do
    try_again = true
    ::EM.next_tick { fiber.resume if fiber } if fiber
  end
  begin
    Mutex.start_watcher unless watching?
    queues = names.map {|n| signal_queue[n] << sig_proc }
    ident_match = owner_ident
    loop do
      start_time = Time.now.to_f
      case timeout = eval_safe(@eval_lock, names, [ident_match,
        ((lock_expire = (Time.now + expire_timeout).to_f)*1000.0).to_i])
      when 'OK'
        @locked_owner_id = ident_match
        @lock_expire = lock_expire
        break
      when 'DD'
        raise MutexError, "deadlock; recursive locking #{ident_match}"
      else
        expire_time = start_time + (timeout/=1000.0)
        timeout = block_timeout if block_timeout && block_timeout < timeout
        if !try_again && timeout > 0
          timer = ::EM::Timer.new(timeout) do
            timer = nil
            ::EM.next_tick { fiber.resume if fiber } if fiber
          end
          fiber = Fiber.current
          Fiber.yield
          fiber = nil
        end
        finish_time = Time.now.to_f
        if try_again || finish_time > expire_time
          block_timeout-= finish_time - start_time if block_timeout
          try_again = false
        else
          return false
        end
      end
    end
    true
  ensure
    timer.cancel if timer
    timer = nil
    queues.each {|q| q.delete sig_proc }
    names.each {|n| signal_queue.delete(n) if signal_queue[n].empty? }
  end
end

#locked?Boolean

Returns ‘true` if this semaphore (at least one of locked `names`) is currently being held by some owner.

Returns:

  • (Boolean)


52
53
54
55
56
57
58
# File 'lib/redis/em-mutex/script_handler.rb', line 52

def locked?
  if sha1 = @eval_is_locked
    1 == eval_safe(sha1, @ns_names)
  else
    redis_pool.exists @ns_names.first
  end
end

#owned?Boolean

Returns ‘true` if this semaphore (all the locked `names`) is currently being held by calling owner. This is the method you should use to check if lock is still held and valid.

Returns:

  • (Boolean)


62
63
64
65
66
# File 'lib/redis/em-mutex/script_handler.rb', line 62

def owned?
  !!if owner_ident == (lock_full_ident = @locked_owner_id)
    redis_pool.mget(*@ns_names).all? {|v| v == lock_full_ident}
  end
end

#owner_identObject



47
48
49
# File 'lib/redis/em-mutex/script_handler.rb', line 47

def owner_ident
  "#{uuid}$#$$@#{Fiber.current.__id__}"
end

#refresh(expire_timeout = nil) ⇒ Object

Refreshes lock expiration timeout. Returns ‘true` if refresh was successfull. Returns `false` if the semaphore wasn’t locked or when it was locked but it has expired.



127
128
129
130
131
132
133
134
135
136
# File 'lib/redis/em-mutex/script_handler.rb', line 127

def refresh(expire_timeout=nil)
  if @lock_expire && owner_ident == (lock_full_ident = @locked_owner_id)
    lock_expire = (Time.now + (expire_timeout.to_f.nonzero? || self.expire_timeout)).to_f
    !!if 1 == eval_safe(@eval_refresh, @ns_names, [lock_full_ident, (lock_expire*1000.0).to_i])
      @lock_expire = lock_expire
    end
  else
    false
  end
end

#try_lockObject

Attempts to obtain the lock and returns immediately. Returns ‘true` if the lock was granted. Use Mutex#expire_timeout= to set lock expiration time in secods. Otherwise global Mutex.default_expire is used.

This method captures expired semaphores only in “script” implementation and therefore it should NEVER be used under normal circumstances. Use Mutex#lock with block_timeout = 0 to obtain expired lock without blocking.



115
116
117
118
119
120
121
122
# File 'lib/redis/em-mutex/script_handler.rb', line 115

def try_lock
  lock_expire = (Time.now + expire_timeout).to_f
  lock_full_ident = owner_ident
  !!if 1 == eval_safe(@eval_try_lock, @ns_names, [lock_full_ident, (lock_expire*1000.0).to_i])
    @lock_expire = lock_expire
    @locked_owner_id = lock_full_ident
  end
end

#unlock!Object

Releases the lock. Returns self on success. Returns ‘false` if the semaphore wasn’t locked or when it was locked but it has expired and now it’s got a new owner. In case of unlocking multiple name semaphore this method returns self only when all of the names have been unlocked successfully.



143
144
145
146
147
148
149
# File 'lib/redis/em-mutex/script_handler.rb', line 143

def unlock!
  if (lock_expire = @lock_expire) && owner_ident == (lock_full_ident = @locked_owner_id)
    @locked_owner_id = @lock_expire = nil
    removed = eval_safe(@eval_unlock, @ns_names, [lock_full_ident, SIGNAL_QUEUE_CHANNEL, @marsh_names])
  end
  return removed == @ns_names.length && self
end