Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Remove delegation from WeakRef. See #6308 in MRI's bug tracker. #406

Open
wants to merge 1 commit into from

1 participant

@headius
  • lib/weakref.rb: Modify weakref to not use delegation. See #6308.
  • test/test_weakref.rb: Alter tests that use delegation.
@headius headius * lib/weakref.rb: Modify weakref to not use delegation. See #6308.
* test/test_weakref.rb: Alter tests that use delegation.
636b0bd
@headius

Updated based on discussions in https://bugs.ruby-lang.org/issues/6308

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 31, 2013
  1. @headius

    * lib/weakref.rb: Modify weakref to not use delegation. See #6308.

    headius authored
    * test/test_weakref.rb: Alter tests that use delegation.
This page is out of date. Refresh to see the latest.
Showing with 73 additions and 48 deletions.
  1. +62 −30 lib/weakref.rb
  2. +11 −18 test/test_weakref.rb
View
92 lib/weakref.rb
@@ -3,20 +3,18 @@
# 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 # 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)
+# foo = Object.new # create a new object instance
+# p foo.to_s # original's class
+# foo = WeakReference.new(foo) # reassign foo with WeakReference instance
+# p foo.get.to_s # should be same class
+# GC.start # start the garbage collector
+# p foo.get # should be nil (recycled)
#
# == Example
#
-# With help from WeakRef, we can implement our own redimentary WeakHash class.
+# With help from WeakReference, we can implement our own rudimentary WeakHash class.
#
# We will call it WeakHash, since it's really just a Hash except all of it's
# keys and values can be garbage collected.
@@ -25,42 +23,75 @@
#
# class WeakHash < Hash
# def []= key, obj
-# super WeakRef.new(key), WeakRef.new(obj)
+# super WeakReference.new(key), WeakReference.new(obj)
+# end
+#
+# def [] key
+# super(key).get
# end
# end
#
-# This is just a simple implementation, we've opened the Hash class and changed
-# Hash#store to create a new WeakRef object with +key+ and +obj+ parameters
+# This is just a simple implementation, we've extend the Hash class and changed
+# Hash#store to create a new WeakReference object with +key+ and +obj+ parameters
# before passing them as our key-value pair to the hash.
#
-# With this you will have to limit your self to String key's, otherwise you
-# will get an ArgumentError because WeakRef cannot create a finalizer for a
-# Symbol. Symbols are immutable and cannot be garbage collected.
-#
# Let's see it in action:
#
# omg = "lol"
# c = WeakHash.new
# c['foo'] = "bar"
# c['baz'] = Object.new
-# c['qux'] = omg
+# c[omg] = "rofl"
# puts c.inspect
-# #=> {"foo"=>"bar", "baz"=>#<Object:0x007f4ddfc6cb48>, "qux"=>"lol"}
+# #=> {"foo"=>"bar", "baz"=>#<Object:0x007f4ddfc6cb48>, "lol"=>"rofl"}
#
# # Now run the garbage collector
# GC.start
# c['foo'] #=> nil
# c['baz'] #=> nil
-# c['qux'] #=> nil
-# omg #=> "lol"
-#
-# puts c.inspect
-# #=> WeakRef::RefError: Invalid Reference - probably recycled
+# c[omg] #=> "rofl"
#
-# You can see the local variable +omg+ stayed, although it's reference in our
-# hash object was garbage collected, along with the rest of the keys and
-# values. Also, when we tried to inspect our hash, we got a WeakRef::RefError,
-# this is because these objects were also garbage collected.
+# You can see the key associated with our local variable omg remained available
+# while all other keys have been collected.
+
+class WeakReference
+
+ @@__map = ::ObjectSpace::WeakMap.new
+
+ ##
+ # Creates a weak reference to +orig+
+ #
+ # Raises an ArgumentError if the given +orig+ is immutable, such as Symbol,
+ # Fixnum, or Float.
+
+ def initialize(orig)
+ case orig
+ when true, false, nil
+ @delegate_sd_obj = orig
+ else
+ @@__map[self] = orig
+ end
+ end
+
+ ##
+ # Retrieve the object referenced by this WeakReference, or nil if the object
+ # has been collected.
+
+ def get # :nodoc:
+ @@__map[self] or defined?(@delegate_sd_obj) ? @delegate_sd_obj : nil
+ end
+ alias __getobj__ get
+
+ ##
+ # Returns true if the referenced object is still alive.
+
+ def weakref_alive?
+ !!(@@__map[self] or defined?(@delegate_sd_obj))
+ end
+end
+
+# The old WeakRef class is deprecated since it can lead to hard-to-diagnose
+# errors when the referenced object gets collected.
class WeakRef < Delegator
@@ -80,6 +111,7 @@ class RefError < StandardError
# Fixnum, or Float.
def initialize(orig)
+ warn "WeakRef is deprecated. Use WeakReference. See https://bugs.ruby-lang.org/issues/6308."
case orig
when true, false, nil
@delegate_sd_obj = orig
@@ -109,9 +141,9 @@ def weakref_alive?
# require 'thread'
foo = Object.new
p foo.to_s # original's class
- foo = WeakRef.new(foo)
- p foo.to_s # should be same class
+ foo = WeakReference.new(foo)
+ p foo.get.to_s # should be same class
ObjectSpace.garbage_collect
ObjectSpace.garbage_collect
- p foo.to_s # should raise exception (recycled)
+ p foo.get.to_s # should raise exception (get returns nil)
end
View
29 test/test_weakref.rb
@@ -3,26 +3,23 @@
require_relative './ruby/envutil'
class TestWeakRef < Test::Unit::TestCase
- def make_weakref(level = 10)
+ def make_weakref
obj = Object.new
str = obj.to_s
- level.times {obj = WeakRef.new(obj)}
- return WeakRef.new(obj), str
+ return WeakReference.new(obj), str
end
def test_ref
weak, str = make_weakref
- assert_equal(str, weak.to_s)
+ assert_equal(str, weak.get.to_s)
end
def test_recycled
weak, str = make_weakref
- assert_nothing_raised(WeakRef::RefError) {weak.to_s}
- assert_predicate(weak, :weakref_alive?)
+ assert_equal str, weak.get.to_s
ObjectSpace.garbage_collect
ObjectSpace.garbage_collect
- assert_raise(WeakRef::RefError) {weak.to_s}
- assert_not_predicate(weak, :weakref_alive?)
+ assert_equal nil, weak.get
end
def test_not_reference_different_object
@@ -31,17 +28,13 @@ def test_not_reference_different_object
3.times do
obj = Object.new
def obj.foo; end
- weakrefs << WeakRef.new(obj)
+ weakrefs << WeakReference.new(obj)
ObjectSpace.garbage_collect
end
- assert_nothing_raised(NoMethodError, bug7304) {
- weakrefs.each do |weak|
- begin
- weak.foo
- rescue WeakRef::RefError
- end
- end
- }
+ weakrefs.each do |weak|
+ obj = weak.get
+ assert obj == nil || obj.respond_to?(:foo)
+ end
end
def test_weakref_finalize
@@ -50,7 +43,7 @@ def test_weakref_finalize
require 'weakref'
obj = Object.new
3.times do
- WeakRef.new(obj)
+ WeakReference.new(obj)
ObjectSpace.garbage_collect
end
}, bug7304
Something went wrong with that request. Please try again.