class ReentrantFlock

Please note that:

“` fp = File.open('a', 'w') fp.flock(File::LOCK_EX) fp.flock(File::LOCK_EX) # does not block fp = File.open('a', 'w') fp.flock(File::LOCK_EX) # block “`

That is, File#flock is orginally reentrant for the same file object. On linux, file lock is associated with file descriptor, so another file descriptor is required to get blocked. This version holds the same file object, so might be useless. I may delete this. Using flock directly should be enough.

Constants

VERSION

Attributes

fp[R]

Public Class Methods

flock(fp, operation) click to toggle source

Note that File#flock is automatically unlocked when a file is closed, but ReentrantFlock.flock cannot automatically reset internal counts when a file is closed. Use ReentrantFlock.synchronize to assure decrementing internal counts.

@param [File] fp @param [PARAM] operation See File#flock @return see File#flock

# File lib/reentrant_flock.rb, line 17
def flock(fp, operation)
  if (operation & File::LOCK_UN) > 0
    unlock(fp)
  else
    lock(fp, operation)
  end
end
new(fp) click to toggle source
# File lib/reentrant_flock.rb, line 110
def initialize(fp)
  @fp = fp
  @mutex = Mutex.new
  @counts = Hash.new(0)
end
self_locked?(fp) click to toggle source

@param fp [File] @return [Boolean] true if locked by self

# File lib/reentrant_flock.rb, line 43
def self_locked?(fp)
  k = key(fp)
  Thread.current.key?(k) and Thread.current[k] >= 1
end
synchronize(fp, operation) { || ... } click to toggle source

@param fp [File] @param [PARAM] operation See File#flock @yield @raise [AlreadyLocked] if already locked and LOCK_NB operation is specified @return [Object] the result of block

# File lib/reentrant_flock.rb, line 30
def synchronize(fp, operation)
  raise 'Must be called with a block' unless block_given?

  begin
    raise AlreadyLocked if lock(fp, operation) == false
    yield
  ensure
    unlock(fp)
  end
end

Private Class Methods

decr(key) click to toggle source
# File lib/reentrant_flock.rb, line 81
def decr(key)
  Thread.current[key] ||= 0
  Thread.current[key] -= 1
end
del(key) click to toggle source
# File lib/reentrant_flock.rb, line 86
def del(key)
  Thread.current[key] = nil
end
incr(key) click to toggle source
# File lib/reentrant_flock.rb, line 76
def incr(key)
  Thread.current[key] ||= 0
  Thread.current[key] += 1
end
key(fp) click to toggle source
# File lib/reentrant_flock.rb, line 72
def key(fp)
  "reentrant_flock_#{fp.path}"
end
lock(fp, operation) click to toggle source

@return [0] if current thread holds the lock @return [false] if another already locked (LOCK_NB)

# File lib/reentrant_flock.rb, line 52
def lock(fp, operation)
  c = incr(key(fp))
  if c <= 1
    # flock returns 0 if successfully locked.
    # LOCK_NB returns false if somebody else already locked
    fp.flock(operation)
  else
    0
  end
end
unlock(fp) click to toggle source
# File lib/reentrant_flock.rb, line 63
def unlock(fp)
  k = key(fp)
  c = decr(k)
  if c <= 0
    fp.flock(File::LOCK_UN)
    del(k)
  end
end

Public Instance Methods

flock(operation) click to toggle source

@param [PARAM] operation See File#flock @return see File#flock

# File lib/reentrant_flock.rb, line 118
def flock(operation)
  if (operation & File::LOCK_UN) > 0
    unlock
  else
    lock(operation)
  end
end
self_locked?() click to toggle source

@return [Boolean] true if locked by self

# File lib/reentrant_flock.rb, line 142
def self_locked?
  @mutex.synchronize { @counts[Thread.current] >= 1 }
end
synchronize(operation) { || ... } click to toggle source

@param [PARAM] operation See File#flock @yield @raise [AlreadyLocked] if already locked and LOCK_NB operation is specified @return [Object] the result of block

# File lib/reentrant_flock.rb, line 130
def synchronize(operation)
  raise 'Must be called with a block' unless block_given?

  begin
    raise AlreadyLocked if lock(operation) == false
    yield
  ensure
    unlock
  end
end

Private Instance Methods

decr() click to toggle source
# File lib/reentrant_flock.rb, line 171
def decr
  @mutex.synchronize { @counts[Thread.current] -= 1 }
end
del() click to toggle source
# File lib/reentrant_flock.rb, line 175
def del
  @mutex.synchronize { @counts.delete(Thread.current) }
end
incr() click to toggle source
# File lib/reentrant_flock.rb, line 167
def incr
  @mutex.synchronize { @counts[Thread.current] += 1 }
end
lock(operation) click to toggle source
# File lib/reentrant_flock.rb, line 148
def lock(operation)
  c = incr
  if c <= 1
    # flock returns 0 if successfully locked.
    # LOCK_NB returns false if somebody else already locked
    fp.flock(operation)
  else
    0
  end
end
unlock() click to toggle source
# File lib/reentrant_flock.rb, line 159
def unlock
  c = decr
  if c <= 0
    fp.flock(File::LOCK_UN)
    del
  end
end