Skip to content

Commit

Permalink
Fix incorrect download locking.
Browse files Browse the repository at this point in the history
  • Loading branch information
reitermarkus committed Nov 23, 2024
1 parent f0c746a commit 863eb7f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
3 changes: 1 addition & 2 deletions Library/Homebrew/download_strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,7 @@ def fetch(timeout: nil)
raise Timeout::Error, "Timed out downloading #{self.url}: #{e}"
end
ensure
download_lock&.unlock
download_lock&.path&.unlink
download_lock&.unlock(unlink: true)
end

def clear_cache
Expand Down
29 changes: 21 additions & 8 deletions Library/Homebrew/lock_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,30 @@ def initialize(type, locked_path)
@lockfile = nil
end

sig { void }
def lock
@path.parent.mkpath
create_lockfile
return if @lockfile.flock(File::LOCK_EX | File::LOCK_NB)
ignore_interrupts do
create_lockfile

raise OperationInProgressError, @locked_path
next if @lockfile.flock(File::LOCK_EX | File::LOCK_NB)

raise OperationInProgressError, @locked_path
end
end

def unlock
return if @lockfile.nil? || @lockfile.closed?
sig { params(unlink: T::Boolean).void }
def unlock(unlink: false)
ignore_interrupts do
next if @lockfile.nil? || @lockfile.closed?

@lockfile.flock(File::LOCK_UN)
@lockfile.close

@lockfile.flock(File::LOCK_UN)
@lockfile.close
if unlink
@path.unlink if @path.exist?
@lockfile = nil
end
end
end

def with_lock
Expand All @@ -42,6 +53,8 @@ def with_lock
def create_lockfile
return if @lockfile.present? && !@lockfile.closed?

@path.dirname.mkpath

begin
@lockfile = @path.open(File::RDWR | File::CREAT)
rescue Errno::EMFILE
Expand Down
29 changes: 25 additions & 4 deletions Library/Homebrew/test/lock_file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@
RSpec.describe LockFile do
subject(:lock_file) { described_class.new(:lock, Pathname("foo")) }

let(:lock_file_copy) { described_class.new(:lock, Pathname("foo")) }

describe "#lock" do
it "does not raise an error when already locked" do
it "ensures the lock file is created" do
expect(lock_file.path).not_to exist
lock_file.lock
expect(lock_file.path).to exist
end

it "does not raise an error when the same instance is locked multiple times" do
lock_file.lock

expect { lock_file.lock }.not_to raise_error
end

it "raises an error if a lock already exists" do
it "raises an error if another instance is already locked" do
lock_file.lock

expect do
described_class.new(:lock, Pathname("foo")).lock
lock_file_copy.lock
end.to raise_error(OperationInProgressError)
end
end
Expand All @@ -30,7 +38,20 @@
lock_file.lock
lock_file.unlock

expect { described_class.new(:lock, Pathname("foo")).lock }.not_to raise_error
expect { lock_file_copy.lock }.not_to raise_error
end

it "allows deleting a lock file only by the instance that locked it" do
lock_file.lock
expect(lock_file.path).to exist

expect(lock_file_copy.path).to exist
lock_file_copy.unlock(unlink: true)
expect(lock_file_copy.path).to exist
expect(lock_file.path).to exist

lock_file.unlock(unlink: true)
expect(lock_file.path).not_to exist
end
end
end

0 comments on commit 863eb7f

Please sign in to comment.