lib/ref/weak_reference/pure_ruby.rb
module Ref
# This is a pure ruby implementation of a weak reference. It is much more
# efficient than the WeakRef implementation bundled in MRI 1.8 and 1.9
# subclass Delegator which is very heavy to instantiate and utilizes a
# because it does not fair amount of memory under Ruby 1.8.
class WeakReference < Reference
class ReferencePointer
def initialize(object)
@referenced_object_id = object.__id__
add_backreference(object)
end
def cleanup
obj = ObjectSpace._id2ref(@referenced_object_id) rescue nil
remove_backreference(obj) if obj
end
def object
obj = ObjectSpace._id2ref(@referenced_object_id)
obj if verify_backreferences(obj)
rescue RangeError
nil
end
private
# Verify that the object is the same one originally set for the weak reference.
def verify_backreferences(obj) #:nodoc:
return nil unless supports_backreference?(obj)
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
backreferences && backreferences.include?(object_id)
end
# Add a backreference to the object.
def add_backreference(obj) #:nodoc:
return unless supports_backreference?(obj)
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
unless backreferences
backreferences = []
obj.instance_variable_set(:@__weak_backreferences__, backreferences)
end
backreferences << object_id
end
# Remove backreferences from the object.
def remove_backreference(obj) #:nodoc:
return unless supports_backreference?(obj)
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
if backreferences
backreferences.dup.delete(object_id)
obj.send(:remove_instance_variable, :@__weak_backreferences__) if backreferences.empty?
end
end
def supports_backreference?(obj)
obj.respond_to?(:instance_variable_get) && obj.respond_to?(:instance_variable_defined?)
rescue NoMethodError
false
end
end
@@weak_references = {}
@@lock = Monitor.new
# Finalizer that cleans up weak references when references are destroyed.
@@reference_finalizer = lambda do |object_id|
@@lock.synchronize do
reference_pointer = @@weak_references.delete(object_id)
reference_pointer.cleanup if reference_pointer
end
end
# Create a new weak reference to an object. The existence of the weak reference
# will not prevent the garbage collector from reclaiming the referenced object.
def initialize(obj) #:nodoc:
@referenced_object_id = obj.__id__
@@lock.synchronize do
@reference_pointer = ReferencePointer.new(obj)
@@weak_references[self.object_id] = @reference_pointer
end
ObjectSpace.define_finalizer(self, @@reference_finalizer)
end
# Get the reference object. If the object has already been garbage collected,
# then this method will return nil.
def object #:nodoc:
if @reference_pointer
obj = @reference_pointer.object
unless obj
@@lock.synchronize do
@@weak_references.delete(object_id)
@reference_pointer.cleanup
@reference_pointer = nil
end
end
obj
end
end
end
end