Skip to content

Commit

Permalink
[rubygems/rubygems] Use a mutex around Checksum::Store @store access
Browse files Browse the repository at this point in the history
Not wrapping to_lock since access to it is single threaded and
read-only at the time of writing the lockfile.

rubygems/rubygems@3b53aa1b12
  • Loading branch information
martinemde authored and matzbot committed Dec 15, 2023
1 parent 1cfe874 commit d9b3909
Showing 1 changed file with 35 additions and 21 deletions.
56 changes: 35 additions & 21 deletions lib/bundler/checksum.rb
Expand Up @@ -63,6 +63,10 @@ def ==(other)

alias_method :eql?, :==

def same_source?(other)
sources.include?(other.sources.first)
end

def match?(other)
other.is_a?(self.class) && other.digest == digest && other.algo == algo
end
Expand Down Expand Up @@ -161,15 +165,17 @@ class Store

def initialize
@store = {}
@store_mutex = Mutex.new
end

def inspect
"#<#{self.class}:#{object_id} size=#{store.size}>"
end

# Replace when the new checksum is from the same source.
# The primary purpose of this registering checksums from gems where there are
# The primary purpose is registering checksums from gems where there are
# duplicates of the same gem (according to full_name) in the index.
#
# In particular, this is when 2 gems have two similar platforms, e.g.
# "darwin20" and "darwin-20", both of which resolve to darwin-20.
# In the Index, the later gem replaces the former, so we do that here.
Expand All @@ -179,16 +185,14 @@ def inspect
# that contain the same gem with different checksums.
def replace(spec, checksum)
return unless checksum

lock_name = spec.name_tuple.lock_name
checksums = (store[lock_name] ||= {})
existing = checksums[checksum.algo]

# we assume only one source because this is used while building the index
if !existing || existing.sources.first == checksum.sources.first
checksums[checksum.algo] = checksum
else
register_checksum(lock_name, checksum)
@store_mutex.synchronize do
existing = fetch_checksum(lock_name, checksum.algo)
if !existing || existing.same_source?(checksum)
store_checksum(lock_name, checksum)
else
merge_checksum(lock_name, checksum, existing)
end
end
end

Expand All @@ -207,7 +211,8 @@ def merge!(other)

def to_lock(spec)
lock_name = spec.name_tuple.lock_name
if checksums = store[lock_name]
checksums = @store[lock_name]
if checksums
"#{lock_name} #{checksums.values.map(&:to_lock).sort.join(",")}"
else
lock_name
Expand All @@ -217,18 +222,27 @@ def to_lock(spec)
private

def register_checksum(lock_name, checksum)
return unless checksum
checksums = (store[lock_name] ||= {})
existing = checksums[checksum.algo]

if !existing
checksums[checksum.algo] = checksum
elsif existing.merge!(checksum)
checksum
else
raise ChecksumMismatchError.new(lock_name, existing, checksum)
@store_mutex.synchronize do
existing = fetch_checksum(lock_name, checksum.algo)
if existing
merge_checksum(lock_name, checksum, existing)
else
store_checksum(lock_name, checksum)
end
end
end

def merge_checksum(lock_name, checksum, existing)
existing.merge!(checksum) || raise(ChecksumMismatchError.new(lock_name, existing, checksum))
end

def store_checksum(lock_name, checksum)
(@store[lock_name] ||= {})[checksum.algo] = checksum
end

def fetch_checksum(lock_name, algo)
@store[lock_name]&.fetch(algo, nil)
end
end
end
end

0 comments on commit d9b3909

Please sign in to comment.