| 
    
       require 'monitor'
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # WeakReference is a class to represent a reference to an object that is not seen by
 
     | 
  
  
     | 
    
       # the tracing phase of the garbage collector.  This allows the referenced
 
     | 
  
  
     | 
    
       # object to be garbage collected as if nothing is referring to it.
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       # The difference between this class and WeakRef is that this class does not
 
     | 
  
  
     | 
    
       # use the delegator pattern and so has an interface more suited for detecting
 
     | 
  
  
     | 
    
       # if the referenced object has been reclaimed.
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       # Usage:
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       #   foo = Object.new
 
     | 
  
  
     | 
    
       #   ref = WeakReference.new(foo)
 
     | 
  
  
     | 
    
       #   ref.object			# should be foo
 
     | 
  
  
     | 
    
       #   ObjectSpace.garbage_collect
 
     | 
  
  
     | 
    
       #   ref.object			# should be nil
 
     | 
  
  
     | 
    
       class WeakReference
 
     | 
  
  
     | 
    
         attr_reader :referenced_object_id
 
     | 
  
  
     | 
    
         
 
     | 
  
  
     | 
    
         # Map of references to the object_id's they refer to.
 
     | 
  
  
     | 
    
         @@referenced_object_ids = {}
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         # Map of object_ids to references to them.
 
     | 
  
  
     | 
    
         @@object_id_references = {}
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         @@monitor = Monitor.new
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         # Finalizer that cleans up weak references when an object is destroyed.
 
     | 
  
  
     | 
    
         @@object_finalizer = lambda do |object_id|
 
     | 
  
  
     | 
    
           @@monitor.synchronize do
 
     | 
  
  
     | 
    
             reference_ids = @@object_id_references[object_id]
 
     | 
  
  
     | 
    
             if reference_ids
 
     | 
  
  
     | 
    
           	  reference_ids.each do |reference_object_id|
 
     | 
  
  
     | 
    
           	    @@referenced_object_ids.delete(reference_object_id)
 
     | 
  
  
     | 
    
           	  end
 
     | 
  
  
     | 
    
           	  @@object_id_references.delete(object_id)
 
     | 
  
  
     | 
    
         	  end
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         # Finalizer that cleans up weak references when references are destroyed.
 
     | 
  
  
     | 
    
         @@reference_finalizer = lambda do |object_id|
 
     | 
  
  
     | 
    
           @@monitor.synchronize do
 
     | 
  
  
     | 
    
             referenced_id = @@referenced_object_ids.delete(object_id)
 
     | 
  
  
     | 
    
             if referenced_id
 
     | 
  
  
     | 
    
               obj = ObjectSpace._id2ref(referenced_object_id) rescue nil
 
     | 
  
  
     | 
    
               if obj
 
     | 
  
  
     | 
    
                 backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
 
     | 
  
  
     | 
    
                 if backreferences
 
     | 
  
  
     | 
    
                   backreferences.delete(object_id)
 
     | 
  
  
     | 
    
                   obj.remove_instance_variable(:@__weak_backreferences__) if backreferences.empty?
 
     | 
  
  
     | 
    
                 end
 
     | 
  
  
     | 
    
               end
 
     | 
  
  
     | 
    
               references = @@object_id_references[referenced_id]
 
     | 
  
  
     | 
    
               if references
 
     | 
  
  
     | 
    
                 references.delete(object_id)
 
     | 
  
  
     | 
    
             	  @@object_id_references.delete(referenced_id) if references.empty?
 
     | 
  
  
     | 
    
           	  end
 
     | 
  
  
     | 
    
         	  end
 
     | 
  
  
     | 
    
           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)
 
     | 
  
  
     | 
    
           @referenced_object_id = obj.__id__
 
     | 
  
  
     | 
    
           ObjectSpace.define_finalizer(obj, @@object_finalizer)
 
     | 
  
  
     | 
    
           ObjectSpace.define_finalizer(self, @@reference_finalizer)
 
     | 
  
  
     | 
    
           @@monitor.synchronize do
 
     | 
  
  
     | 
    
             @@referenced_object_ids[self.__id__] = obj.__id__
 
     | 
  
  
     | 
    
             add_backreference(obj)
 
     | 
  
  
     | 
    
             references = @@object_id_references[obj.__id__]
 
     | 
  
  
     | 
    
             unless references
 
     | 
  
  
     | 
    
               references = []
 
     | 
  
  
     | 
    
               @@object_id_references[obj.__id__] = references
 
     | 
  
  
     | 
    
             end
 
     | 
  
  
     | 
    
             references.push(self.__id__)
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         # Get the reference object. If the object has already been garbage collected,
 
     | 
  
  
     | 
    
         # then this method will return nil.
 
     | 
  
  
     | 
    
         def object
 
     | 
  
  
     | 
    
           obj = nil
 
     | 
  
  
     | 
    
           begin
 
     | 
  
  
     | 
    
             if referenced_object_id == @@referenced_object_ids[self.object_id]
 
     | 
  
  
     | 
    
               obj = ObjectSpace._id2ref(referenced_object_id)
 
     | 
  
  
     | 
    
               obj = nil unless verify_backreferences(obj)
 
     | 
  
  
     | 
    
             end
 
     | 
  
  
     | 
    
           rescue RangeError
 
     | 
  
  
     | 
    
             # Object has been garbage collected.
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
           obj
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         private
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
           def add_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
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
           def verify_backreferences(obj)
 
     | 
  
  
     | 
    
             backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
 
     | 
  
  
     | 
    
             backreferences && backreferences.include?(object_id)
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
       end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       require "delegate"
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # WeakRef is a class to represent a reference to an object that is not seen by the tracing
 
     | 
  
  
     | 
    
       # phase of the garbage collector. This allows the referenced object to be garbage collected
 
     | 
  
  
     | 
    
       # as if nothing is referring to it. Because WeakRef delegates method calls to the referenced
 
     | 
  
  
     | 
    
       # object, it may be used in place of that object, i.e. it is of the same duck type.
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       # If you don't need to use the delegator pattern, you can use WeakReference instead.
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       # Usage:
 
     | 
  
  
     | 
    
       #   foo = Object.new
 
     | 
  
  
     | 
    
       #   foo = Object.new
 
     | 
  
  
     | 
    
       #   p foo.to_s			# original's class
 
     | 
  
  
     | 
    
       #   foo = WeakRef.new(foo)
 
     | 
  
  
     | 
    
       #   p foo.to_s			# should be same class
 
     | 
  
  
     | 
    
       #   ObjectSpace.garbage_collect
 
     | 
  
  
     | 
    
       #   p foo.to_s			# should raise exception (recycled)
 
     | 
  
  
     | 
    
       class WeakRef < Delegator
 
     | 
  
  
     | 
    
         class RefError < StandardError
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
         
 
     | 
  
  
     | 
    
         def initialize(obj)
 
     | 
  
  
     | 
    
           super
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
         
 
     | 
  
  
     | 
    
         def __getobj__
 
     | 
  
  
     | 
    
           obj = @reference.object
 
     | 
  
  
     | 
    
           Kernel::raise(RefError, "Invalid Reference - probably recycled", Kernel::caller(1)) unless obj
 
     | 
  
  
     | 
    
           obj
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
         
 
     | 
  
  
     | 
    
         def __setobj__(obj)
 
     | 
  
  
     | 
    
           @reference = WeakReference.new(obj)
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
         
 
     | 
  
  
     | 
    
         def weakref_alive?
 
     | 
  
  
     | 
    
           !!@reference.object
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       if __FILE__ == $0
 
     | 
  
  
     | 
    
         foo = Object.new
 
     | 
  
  
     | 
    
         p foo.to_s			# original's class
 
     | 
  
  
     | 
    
         foo = WeakRef.new(foo)
 
     | 
  
  
     | 
    
         p foo.to_s			# should be same class
 
     | 
  
  
     | 
    
         ObjectSpace.garbage_collect
 
     | 
  
  
     | 
    
         ObjectSpace.garbage_collect
 
     | 
  
  
     | 
    
         p foo.to_s			# should raise exception (recycled)
 
     | 
  
  
     | 
    
       end
 
     |