Commit f1c6772e authored by Mehmet Emin INAC's avatar Mehmet Emin INAC

Fix the race-condition issue on `IgnorableColumns`

Since the Ruby threads are preemptive on the user level, the scheduler
can decide to do the context switching to give the execution to another
thread after the conditional check. If this happens, two threads will
generate the exact same object. We can avoid this by just synchronizing
the whole block.

Since the `Mutex` is not `reentrant` in Ruby, I've also changed the way
we synchronize the critical section with `Monitor`.
parent 1928b708
......@@ -31,15 +31,13 @@ module IgnorableColumns
alias_method :ignore_column, :ignore_columns
def ignored_columns_details
unless defined?(@ignored_columns_details)
IGNORE_COLUMN_MUTEX.synchronize do
@ignored_columns_details ||= superclass.try(:ignored_columns_details)&.dup || {}
end
end
return @ignored_columns_details if defined?(@ignored_columns_details)
@ignored_columns_details
IGNORE_COLUMN_MONITOR.synchronize do
@ignored_columns_details ||= superclass.try(:ignored_columns_details)&.dup || {}
end
end
IGNORE_COLUMN_MUTEX = Mutex.new
IGNORE_COLUMN_MONITOR = Monitor.new
end
end
......@@ -59,6 +59,14 @@ RSpec.describe IgnorableColumns do
it_behaves_like 'storing removal information'
end
context 'when called on a subclass without setting the ignored columns' do
let(:subclass) { Class.new(record_class) }
it 'does not raise Deadlock error' do
expect { subclass.ignored_columns_details }.not_to raise_error
end
end
it 'defaults to empty Hash' do
expect(subject.ignored_columns_details).to eq({})
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment