We will achieve this by creating a block accepting method to optionally create and then lock a .lock
File of the underlying accessed file.
Why create a .lock
file?
.lock
file is that #flock
might block some operations and require the index node of the file to be consistent. Some operations might change that index node.That being said the shown process also works without creating a lock file by calling File.open(@file_path) { ... }
on the file directly instead. I will also include an example on how to use this for directories instead for files at the end.
You can define the following method to lock a file from read and write access by methods like File.open
and File.read
.
def exclusively_locked_access
begin
@file_locked = true
File.open(F"#{file_path}.lock"), 'w+') do |f|
f.flock(File::LOCK_EX)
yield
end
ensure
@file_locked = false
end
end
#flock
requests a lock and waits until it can lock the file.
LOCK_EX
for flock
will create an exclusive lock for that file. You can look up
the doc for #flock
Show archive.org snapshot
to find all available locking options.Now we can pass any block to the method to safely every file access you want to have locked like so
exclusively_locked_access do
file_content = File.read(@file_path)
end
File.open
will take care of closing and unlocking the file.begin
and rescue
if something might go wrong:begin
f = File.open(file, File::CREAT)
f.flock(File::LOCK_EX)
yield
ensure
f.flock(File::LOCK_UN)
f.close
end
If you might run into deadlocks it can be safer to ensure a timeout with using Timeout::timeout(0.1) { f.flock(File::LOCK_EX) }
This can also achieved similarly:
def exclusively_folder_locked_access
directory = File.open(Paths::GEM_STORAGE)
directory.flock(File::LOCK_EX)
yield
directory.flock(File::LOCK_UN)
end
Since you can't write a directory you also don't have to call #close
on it.