diff options
Diffstat (limited to 'lib/weakref.rb')
| -rw-r--r-- | lib/weakref.rb | 108 |
1 files changed, 42 insertions, 66 deletions
diff --git a/lib/weakref.rb b/lib/weakref.rb index 49b907ba17..c7274f9664 100644 --- a/lib/weakref.rb +++ b/lib/weakref.rb @@ -1,83 +1,59 @@ -# Weak Reference class that does not bother GCing. +# frozen_string_literal: true +require "delegate" + +# Weak Reference class that allows a referenced object to be +# garbage-collected. +# +# A WeakRef may be used exactly like the object it references. # # 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) +# +# foo = Object.new # create a new object instance +# p foo.to_s # original's class +# foo = WeakRef.new(foo) # reassign foo with WeakRef instance +# p foo.to_s # should be same class +# GC.start # start the garbage collector +# p foo.to_s # should raise exception (recycled) +# -require "delegate" +class WeakRef < Delegator + # The version string + VERSION = "0.1.4" -class WeakRef<Delegator + ## + # RefError is raised when a referenced object has been recycled by the + # garbage collector - class RefError<StandardError + class RefError < StandardError end - @@id_map = {} # obj -> [ref,...] - @@id_rev_map = {} # ref -> obj - @@final = lambda{|id| - __old_status = Thread.critical - Thread.critical = true - begin - rids = @@id_map[id] - if rids - for rid in rids - @@id_rev_map.delete(rid) - end - @@id_map.delete(id) - end - rid = @@id_rev_map[id] - if rid - @@id_rev_map.delete(id) - @@id_map[rid].delete(id) - @@id_map.delete(rid) if @@id_map[rid].empty? - end - ensure - Thread.critical = __old_status - end - } + @@__map = ::ObjectSpace::WeakMap.new + + ## + # Creates a weak reference to +orig+ def initialize(orig) - super - @__id = orig.__id__ - ObjectSpace.define_finalizer orig, @@final - ObjectSpace.define_finalizer self, @@final - __old_status = Thread.critical - begin - Thread.critical = true - @@id_map[@__id] = [] unless @@id_map[@__id] - ensure - Thread.critical = __old_status + case orig + when true, false, nil + @delegate_sd_obj = orig + else + @@__map[self] = orig end - @@id_map[@__id].push self.__id__ - @@id_rev_map[self.__id__] = @__id + super end - def __getobj__ - unless @@id_rev_map[self.__id__] == @__id - raise RefError, "Illegal Reference - probably recycled", caller(2) - end - begin - ObjectSpace._id2ref(@__id) - rescue RangeError - raise RefError, "Illegal Reference - probably recycled", caller(2) - end + def __getobj__(&_block) # :nodoc: + @@__map[self] or defined?(@delegate_sd_obj) ? @delegate_sd_obj : + Kernel::raise(RefError, "Invalid Reference - probably recycled", Kernel::caller(2)) end - def weakref_alive? - @@id_rev_map[self.__id__] == @__id + def __setobj__(obj) # :nodoc: end -end -if __FILE__ == $0 - require 'thread' - 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) + ## + # Returns true if the referenced object is still alive. + + def weakref_alive? + @@__map.key?(self) or defined?(@delegate_sd_obj) + end end |
