Method: Futex#open

Defined in:
lib/futex.rb

#open(exclusive = true) ⇒ Object

Open the file. By default the file will be locked for exclusive access, which means that absolutely no other process will be able to do the same. This type of access (exclusive) is supposed to be used when you are making changes to the file. However, very often you may need just to read it and it’s OK to let many processes do the reading at the same time, provided none of them do the writing. In that case you should call this method open() with false first argument, which will mean “shared” access. Many threads and processes may have shared access to the same lock file, but they all will stop and wait if one of them will require an “exclusive” access. This mechanism is inherited from POSIX, read about it <a href=“man7.org/linux/man-pages/man2/flock.2.html”>here</a>.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/futex.rb', line 95

def open(exclusive = true)
  FileUtils.mkdir_p(File.dirname(@lock))
  step = (1 / @sleep).to_i
  start = Time.now
  prefix = exclusive ? '' : 'non-'
  b = badge(exclusive)
  Thread.current.thread_variable_set(:futex_lock, @lock)
  Thread.current.thread_variable_set(:futex_badge, b)
  open_synchronized(@lock) do |f|
    cycle = 0
    loop do
      if f.flock((exclusive ? File::LOCK_EX : File::LOCK_SH) | File::LOCK_NB)
        Thread.current.thread_variable_set(:futex_cycle, nil)
        Thread.current.thread_variable_set(:futex_time, nil)
        break
      end
      sleep(@sleep)
      cycle += 1
      Thread.current.thread_variable_set(:futex_cycle, cycle)
      Thread.current.thread_variable_set(:futex_time, Time.now - start)
      if Time.now - start > @timeout
        raise CantLock.new("#{b} can't get #{prefix}exclusive access \
to the file #{@path} because of the lock at #{@lock}, after #{age(start)} \
of waiting: #{IO.read(@lock)} (modified #{age(File.mtime(@lock))} ago)",
        File.mtime(@lock))
      end
      next unless (cycle % step).zero? && Time.now - start > @timeout / 2
      debug("#{b} still waiting for #{prefix}exclusive \
access to #{@path}, #{age(start)} already: #{IO.read(@lock)} \
(modified #{age(File.mtime(@lock))} ago)")
    end
    debug("Locked by #{b} in #{age(start)}, #{prefix}exclusive: \
#{@path} (attempt no.#{cycle})")
    IO.write(@lock, b)
    acq = Time.now
    res = block_given? ? yield(@path) : nil
    debug("Unlocked by #{b} in #{age(acq)}, #{prefix}exclusive: #{@path}")
    res
  end
ensure
  Thread.current.thread_variable_set(:futex_cycle, nil)
  Thread.current.thread_variable_set(:futex_time, nil)
  Thread.current.thread_variable_set(:futex_lock, nil)
  Thread.current.thread_variable_set(:futex_badge, nil)
end