This repository has been archived by the owner on Mar 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
110 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
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 |