I understand the general concept of file locking, but I'm afraid the details elude me. I want to open a file for writing, get an exclusive lock, but wait some number of seconds before giving up on getting the lock. I've gotten this far:
File.open(path, 'a') do |file|
if file.flock(File::LOCK_EX | File::LOCK_NB)
file.seek(0, IO::SEEK_END)
file.write('my content')
else
puts "Could not acquire lock, another process is using the file"
end
end
How do I wait for the lock for x number of seconds?
You can setup a timeout handling yourself similar to the following code. Here, we open the file and try to lock it. If the lock failed, we sleep 0.1 second and try again until we have waited at most 5 seconds (by default). If the timeout strikes, we raise an error.
class Timeout < RuntimeError; end
def locked_file(path, *args, timeout: 5)
File.open(path, *args) do |file|
started = Process.Process.clock_gettime(Process::CLOCK_MONOTONIC)
while Process.clock_gettime(Process::CLOCK_MONOTONIC) - started < timeout do
if file.flock(File::LOCK_EX | File::LOCK_NB)
return yield(file)
else
sleep .1
end
end
raise Timeout, "could not lock #{path} within #{timeout} seconds"
end
end
locked_file(path, 'a', timeout: 5) do |file|
file.write('my content')
end
Note that when opening a file in append mode, the operating system already ensures that concurrent writes to the same file do not overlap. Thus, if multiple processes open a file in append mode, they can just write to it without any further locking.
However, if any process writes to the file in a different mode than append, this will not hold anymore and you will likely see corruption.
Also, some network filesystems (such as NFS or must fuse filesystems) might not guarantee this property (but will likely also not support flock). SMB/CIFS is safe here though.
In any case, when opening a file in append mode, it is not necessary to seek to the end as the file pointer is automatically set to the end of the file. On the contrary, seeking may have a negative effect as it can cause overlapping writes (that is: as long as the seek is actually processed, which I'm not really sure is possible with a file opened in append mode.)