I'm looking at Ruby WeakRef, and it seems that the way the API is written has an implied race condition, though it seems very unlikely to hit.
The basic usage implied by the API is:
obj = Object.new
foo = WeakRef.new(obj)
# Later on:
if (foo.weakref_alive?)
puts "I can allegedly use #{foo.to_s} now"
end
# Or even:
obj2 = foo.__getobj__ if foo.weakref_alive?
The problem lies in the fact that we don't have control over when garbage collection may happen, as an example, consider another thread running that regularly calls GC.start.
If we have garbage collection happening between the weakref_alive? check and the usage of the object, then we will end up hitting the RefError exception.
(I would actually expect that any large application that uses weakref - particularly those that are multithreaded - would hit RefErrors occasionally due to this)
I'm surprised there's no way to safely get the object in an atomic way if the object is available at the moment we check it.
So the question is first, am I overconcerned? Is there some reason we don't have to ever worry about a GC happening if we fetch the object right away after checking it (as in the second example)? And if not, then that gives us the second question, of the best way to safely work with weakrefs.
Right now I've added an 'obj' method to the class as one way to deal with it:
require 'weakref'
class WeakRef
def obj
begin
return self.__getobj__
rescue RefError
return nil
end
end
end
But unnecessary 'rescue' statements kind of bug me. I suppose we could also:
require 'weakref'
class WeakRef
def obj
savegc = GC.disable
obj = self.weakref_alive? ? self.__getobj__ : nil
GC.enable if savegc
return obj
end
end
But I'm skeptical that it's low-cost to just disable and re-enable the garbage collection, much less whether this is a completely atomic operation.
Any advice from ruby GC experts?