lib/ref/soft_reference.rb
module Ref
# A SoftReference represents 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.
#
# A SoftReference differs from a WeakReference in that the garbage collector
# is not so eager to reclaim soft references so they should persist longer.
#
# === Example usage:
#
# foo = Object.new
# ref = Ref::SoftReference.new(foo)
# ref.object # should be foo
# ObjectSpace.garbage_collect
# ref.object # should be foo
# ObjectSpace.garbage_collect
# ObjectSpace.garbage_collect
# ref.object # should be nil
class SoftReference < Reference
@@strong_references = [{}]
@@gc_flag_set = false
# Number of garbage collection cycles after an object is used before a reference to it can be reclaimed.
MIN_GC_CYCLES = 10
@@lock = Monitor.new
@@finalizer = lambda do |object_id|
@@lock.synchronize do
while @@strong_references.size >= MIN_GC_CYCLES do
@@strong_references.shift
end
@@strong_references.push({}) if @@strong_references.size < MIN_GC_CYCLES
@@gc_flag_set = false
end
end
# Create a new soft reference to an object.
def initialize(obj)
@referenced_object_id = obj.__id__
@weak_reference = WeakReference.new(obj)
add_strong_reference(obj)
end
# Get the referenced object. If the object has been reclaimed by the
# garbage collector, then this will return nil.
def object
obj = @weak_reference.object
# add a temporary strong reference each time the object is referenced.
add_strong_reference(obj) if obj
obj
end
private
# Create a strong reference to the object. This reference will live
# for three passes of the garbage collector.
def add_strong_reference(obj) #:nodoc:
@@lock.synchronize do
@@strong_references.last[obj] = true
unless @@gc_flag_set
@@gc_flag_set = true
ObjectSpace.define_finalizer(Object.new, @@finalizer)
end
end
end
end
end