class Synapse::IdentifierLockManager

Generic lock manager that can be used to lock identifiers for exclusive access

Public Class Methods

add(instance) click to toggle source

@api private @param [IdentifierLockManager] instance @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 19
def add(instance)
  @mutex.synchronize do
    @instances[instance] = true
  end
end
instances() click to toggle source

@api private @return [Array]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 10
def instances
  @mutex.synchronize do
    @instances.keys
  end
end
new() click to toggle source

@return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 69
def initialize
  @locks = Hash.new
  @mutex = Mutex.new

  IdentifierLockManager.add self
end
waiters_for_locks_owned_by(thread) click to toggle source

@api private @param [Thread] thread @return [Set]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 28
def waiters_for_locks_owned_by(thread)
  stack = Array.new
  waiters = Set.new

  find_waiters thread, instances, waiters, stack

  waiters
end

Private Class Methods

find_waiters(thread, managers, waiters, stack) click to toggle source

@param [Thread] thread @param [Array] managers @param [Set] waiters @param [Array] stack @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 44
def find_waiters(thread, managers, waiters, stack)
  stack.push thread

  for manager in managers
    for lock in manager.internal_locks
      next unless lock.owned_by? thread

      for waiter in lock.waiters
        # Avoid infinite recursion in the case of an imminent deadlock
        next if stack.include? waiter

        # Skip waiters that are already known
        next unless waiters.add? waiter

        # Recursively find waiters for locks
        find_waiters waiter, managers, waiters, stack
      end
    end
  end

  stack.pop
end

Public Instance Methods

internal_locks() click to toggle source

@api private @return [Array]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 116
def internal_locks
  @mutex.synchronize do
    @locks.values
  end
end
obtain_lock(identifier) click to toggle source

Obtains a lock for the given identifier, blocking until the lock is obtained

@param [Object] identifier @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 89
def obtain_lock(identifier)
  loop do
    lock = lock_for identifier

    return if lock.lock
    remove_lock identifier, lock
  end
end
owned?(identifier) click to toggle source

Returns true if the calling thread holds the lock for the given identifier

@param [Object] identifier @return [Boolean]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 80
def owned?(identifier)
  lock_available?(identifier) && lock_for(identifier).owned?
end
release_lock(identifier) click to toggle source

Releases a lock for the given identifier

@raise [RuntimeError] If no lock was ever obtained for the identifier @param [Object] identifier @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 103
def release_lock(identifier)
  unless lock_available? identifier
    raise RuntimeError
  end

  lock = lock_for identifier
  lock.unlock

  try_dispose identifier, lock
end

Private Instance Methods

lock_available?(identifier) click to toggle source

@param [Object] identifier @return [Boolean]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 158
def lock_available?(identifier)
  @mutex.synchronize do
    @locks.has_key? identifier
  end
end
lock_for(identifier) click to toggle source

@param [Object] identifier @return [DisposableLock]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 146
def lock_for(identifier)
  @mutex.synchronize do
    if @locks.has_key? identifier
      @locks.fetch identifier
    else
      @locks.store identifier, DisposableLock.new
    end
  end
end
remove_lock(identifier, lock) click to toggle source

@param [Object] identifier @param [DisposableLock] lock @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 136
def remove_lock(identifier, lock)
  @mutex.synchronize do
    @locks.delete_if do |i, l|
      i == identifier && l == lock
    end
  end
end
try_dispose(identifier, lock) click to toggle source

@param [Object] identifier @param [DisposableLock] lock @return [undefined]

# File lib/synapse/common/concurrency/identifier_lock_manager.rb, line 127
def try_dispose(identifier, lock)
  if lock.try_close
    remove_lock identifier, lock
  end
end