Skip to content
This repository was archived by the owner on Mar 15, 2022. It is now read-only.

Commit 11a0047

Browse files
committed
Fix default block race condition.
By the time `elsif !key?(key)` is called another thread might have created a `key` mapping, then `!key?(key)` check would then fail resulting in `[]` returning `nil` an incorrect `value`. This is most likely to be triggered by a code like this: ThreadSafe::Cache.new {|cache, key| cache[key] = some_value} Related: rails/rails#13961.
1 parent 720e366 commit 11a0047

File tree

1 file changed

+6
-2
lines changed

1 file changed

+6
-2
lines changed

lib/thread_safe/cache.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ def initialize(options = nil, &block)
3535
end
3636

3737
def [](key)
38-
if value = super
38+
if value = super # non-falsy value is an existing mapping, return it right away
3939
value
40-
elsif @default_proc && !key?(key)
40+
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
41+
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
42+
# would be returned)
43+
# note: nil == value check is not technically necessary
44+
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
4145
@default_proc.call(self, key)
4246
else
4347
value

0 commit comments

Comments
 (0)