lib/weakref.rb


DEFINITIONS

This source file includes following functions.


   1  # Weak Reference class that does not bother GCing.
   2  #
   3  # Usage:
   4  #   foo = Object.new
   5  #   foo = Object.new
   6  #   p foo.to_s                  # original's class
   7  #   foo = WeakRef.new(foo)
   8  #   p foo.to_s                  # should be same class
   9  #   ObjectSpace.garbage_collect
  10  #   p foo.to_s                  # should raise exception (recycled)
  11  
  12  require "delegate"
  13  
  14  class WeakRef<Delegator
  15  
  16    class RefError<StandardError
  17    end
  18  
  19    ID_MAP =  {}              # obj -> [ref,...]
  20    ID_REV_MAP =  {}          # ref -> obj
  21    @@final = lambda{|id|
  22      __old_status = Thread.critical
  23      Thread.critical = true
  24      begin
  25        rids = ID_MAP[id]
  26        if rids
  27          for rid in rids
  28            ID_REV_MAP[rid] = nil
  29          end
  30          ID_MAP[id] = nil
  31        end
  32        rid = ID_REV_MAP[id]
  33        if rid
  34          ID_REV_MAP[id] = nil
  35          ID_MAP[rid].delete(id)
  36          ID_MAP[rid] = nil if ID_MAP[rid].empty?
  37        end
  38      ensure
  39        Thread.critical = __old_status
  40      end
  41    }
  42  
  43    def initialize(orig)
  44      super
  45      @__id = orig.__id__
  46      ObjectSpace.define_finalizer orig, @@final
  47      ObjectSpace.define_finalizer self, @@final
  48      __old_status = Thread.critical
  49      begin
  50        Thread.critical = true
  51        ID_MAP[@__id] = [] unless ID_MAP[@__id]
  52      ensure
  53        Thread.critical = __old_status
  54      end
  55      ID_MAP[@__id].push self.__id__
  56      ID_REV_MAP[self.id] = @__id
  57    end
  58  
  59    def __getobj__
  60      unless ID_MAP[@__id]
  61        raise RefError, "Illegal Reference - probably recycled", caller(2)
  62      end
  63      begin
  64        ObjectSpace._id2ref(@__id)
  65      rescue RangeError
  66        raise RefError, "Illegal Reference - probably recycled", caller(2)
  67      end
  68    end
  69  
  70    def weakref_alive?
  71      if ID_MAP[@__id]
  72        true
  73      else
  74        false
  75      end
  76    end
  77  end
  78  
  79  if __FILE__ == $0
  80    require 'thread'
  81    foo = Object.new
  82    p foo.to_s                    # original's class
  83    foo = WeakRef.new(foo)
  84    p foo.to_s                    # should be same class
  85    ObjectSpace.garbage_collect
  86    p foo.to_s                    # should raise exception (recycled)
  87  end