flori / mize

Memoize methods/functions in Ruby
MIT License
3 stars 1 forks source link

Not thread-safe? #2

Open jaynetics opened 4 years ago

jaynetics commented 4 years ago
require 'mize'

class WithMize
  def problem
    v1 = memoized_value
    v2 = memoized_value

    if v1.equal?(v2)
      nil
    else
      [v1, v2]
    end
  end

  memoize method:
  def memoized_value
    Object.new
  end
end

problems = []

2000.times { Thread.new { (prob = WithMize.new.problem) && problems << prob } }

problems # => [[#<Object:0x00007f93d00d28a0>, #<Object:0x00007f93d00d26e8>], [#<Object:0x00007f93c5f8f3f8>, #<Object:0x00007f93d00d1ec8>]]

# compare ivar-based memoization

class WithIvar
  def problem
    v1 = memoized_value
    v2 = memoized_value

    if v1.equal?(v2)
      nil
    else
      [v1, v2]
    end
  end

  def memoized_value
    @memoized_value ||= Object.new
  end
end

problems = []

2000.times { Thread.new { (prob = WithIvar.new.problem) && problems << prob } }

problems # => []
jaynetics commented 4 years ago

it seems as if in some, but not all of the error cases, a memoized value leaks over from another object:

require 'mize'

class Thing
  memoize method:
  def val
    Object.new
  end
end

arrays = []

2000.times { Thread.new { t = Thing.new; arrays << [t.val, t.val] } }

good, bad = arrays.partition { |arr| arr.uniq.count == 1 }

[good.count, bad.count] # => [1998, 2]

bad.flatten.map { |v| good.flatten.include?(v) } # => [false, true, false, false]
jaynetics commented 2 years ago

maybe related: https://github.com/flori/tins/issues/21