How Should I Request a Non-Blocking Lock?
Why doesn't Ruby's File#flock work as expected when separate attempts are made to lock a file? Locking the file in a block is not the correct solution for this issue because the point is to show the behavior of locking on persistent locks. Using File#flock inside a block releases the lock when the block exits, so it doesn't demonstrate the problem properly.
File#flock fails in a variety of ways, especially when requesting a non-blocking lock. Some examples follow.
Failing Examples with File#flock
Infinite wait when using multiple exclusive locks, since #flock doesn't provide a way to timeout a lock request.
# First lock succeeds. f1 = File.open('foo', File::RDWR|File::CREAT, 0644) f1.flock(File::LOCK_EX) # => 0 # This never returns. f2 = File.open('foo', File::RDWR|File::CREAT, 0644) f2.flock(File::LOCK_EX)
Asking for a non-blocking lock while the file is exclusively-locked results in an invalid argument exception.
f1 = File.open('foo', File::RDWR|File::CREAT, 0644) f1.flock(File::LOCK_EX) # => 0 f2 = File.open('foo', File::RDWR|File::CREAT, 0644) f2.flock(File::LOCK_NB) # => Errno::EINVAL: Invalid argument - foo
The documentation says that #flock "Locks or unlocks a file according to locking_constant (a logical or of the values in the table below)." However, a Logical OR raises
Errno::EINVAL
orErrno::EBADF
depending on platform.f1 = File.open('foo', File::RDWR|File::CREAT, 0644) f1.flock(File::LOCK_EX) # => 0 f2 = File.open('foo', File::RDWR|File::CREAT, 0644) f2.flock(File::LOCK_NB || File::LOCK_EX) # => Errno::EINVAL: Invalid argument - foo
Native File#flock Solution Preferred
While one might use the Timeout module to raise Timeout::Error
when unable to obtain an exclusive lock, it seems like File#flock ought to be able to solve this issue natively. So, how is one actually supposed to request an exclusive lock without blocking?