ref 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +35 -0
- data/VERSION +1 -0
- data/ext/java/org/jruby/ext/ref/ReferencesService.java +28 -0
- data/ext/java/org/jruby/ext/ref/RubySoftReference.java +43 -0
- data/ext/java/org/jruby/ext/ref/RubyWeakReference.java +43 -0
- data/lib/org/jruby/ext/ref/references.jar +0 -0
- data/lib/ref.rb +39 -0
- data/lib/ref/abstract_reference_key_map.rb +117 -0
- data/lib/ref/abstract_reference_value_map.rb +127 -0
- data/lib/ref/mock.rb +145 -0
- data/lib/ref/reference.rb +24 -0
- data/lib/ref/reference_queue.rb +89 -0
- data/lib/ref/safe_monitor.rb +50 -0
- data/lib/ref/soft_key_map.rb +26 -0
- data/lib/ref/soft_reference.rb +67 -0
- data/lib/ref/soft_value_map.rb +28 -0
- data/lib/ref/strong_reference.rb +17 -0
- data/lib/ref/weak_key_map.rb +26 -0
- data/lib/ref/weak_reference.rb +26 -0
- data/lib/ref/weak_reference/iron_ruby.rb +14 -0
- data/lib/ref/weak_reference/pure_ruby.rb +91 -0
- data/lib/ref/weak_reference/weak_ref.rb +23 -0
- data/lib/ref/weak_value_map.rb +27 -0
- data/test/reference_key_map_behavior.rb +157 -0
- data/test/reference_key_map_behavior.rbc +4296 -0
- data/test/reference_queue_test.rb +60 -0
- data/test/reference_queue_test.rbc +1954 -0
- data/test/reference_value_map_behavior.rb +137 -0
- data/test/reference_value_map_behavior.rbc +3615 -0
- data/test/soft_key_map_test.rb +13 -0
- data/test/soft_key_map_test.rbc +374 -0
- data/test/soft_reference_test.rb +49 -0
- data/test/soft_reference_test.rbc +1481 -0
- data/test/soft_value_map_test.rb +13 -0
- data/test/soft_value_map_test.rbc +374 -0
- data/test/strong_reference_test.rb +15 -0
- data/test/strong_reference_test.rbc +562 -0
- data/test/test_helper.rb +4 -0
- data/test/weak_key_map_test.rb +13 -0
- data/test/weak_key_map_test.rbc +374 -0
- data/test/weak_reference_test.rb +46 -0
- data/test/weak_reference_test.rbc +1254 -0
- data/test/weak_value_map_test.rb +13 -0
- data/test/weak_value_map_test.rbc +374 -0
- metadata +113 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ref
|
2
|
+
# This class serves as a generic reference mechanism to other objects. The
|
3
|
+
# actual reference can be either a WeakReference, SoftReference, or StrongReference.
|
4
|
+
class Reference
|
5
|
+
# The object id of the object being referenced.
|
6
|
+
attr_reader :referenced_object_id
|
7
|
+
|
8
|
+
# Create a new reference to an object.
|
9
|
+
def initialize(obj)
|
10
|
+
raise NotImplementedError.new("cannot instantiate a generic reference")
|
11
|
+
end
|
12
|
+
|
13
|
+
# Get the referenced object. This could be nil if the reference
|
14
|
+
# is a WeakReference or a SoftReference and the object has been reclaimed by the garbage collector.
|
15
|
+
def object
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
obj = object
|
21
|
+
"<##{self.class.name}: #{obj ? obj.inspect : "##{referenced_object_id} (not accessible)"}>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Ref
|
2
|
+
# This class provides a simple thread safe container to hold a reference queue. Instances
|
3
|
+
# of WeakReference can be added to the queue and as the objects pointed to by those references
|
4
|
+
# are cleaned up by the garbage collector, the references will be added to the queue.
|
5
|
+
#
|
6
|
+
# The reason for using a reference queue is that it tends to be more efficient than adding
|
7
|
+
# individual finalizers to objects and the cleanup code can be handled by a thread outside
|
8
|
+
# of garbage collection.
|
9
|
+
#
|
10
|
+
# In general, you should create your own subclass of WeakReference that contains the logic
|
11
|
+
# needed to complete the cleanup. The object pointed to will have already been cleaned up
|
12
|
+
# and the reference cannot maintain a reference to the object.
|
13
|
+
#
|
14
|
+
# === Example usage:
|
15
|
+
#
|
16
|
+
# class MyRef < References::WeakReference
|
17
|
+
# def cleanup
|
18
|
+
# # Do something...
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# queue = References::ReferenceQueue.new
|
23
|
+
# ref = MyRef.new(Object.new)
|
24
|
+
# queue.monitor(ref)
|
25
|
+
# queue.shift # = nil
|
26
|
+
# ObjectSpace.garbage_collect
|
27
|
+
# r = queue.shift # = ref
|
28
|
+
# r.cleanup
|
29
|
+
class ReferenceQueue
|
30
|
+
def initialize
|
31
|
+
@queue = []
|
32
|
+
@references = {}
|
33
|
+
@lock = SafeMonitor.new
|
34
|
+
@finalizer = lambda do |object_id|
|
35
|
+
@lock.synchronize do
|
36
|
+
ref = @references.delete(object_id)
|
37
|
+
@queue.push(ref) if ref
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Monitor a reference. When the object the reference points to is garbage collected,
|
43
|
+
# the reference will be added to the queue.
|
44
|
+
def monitor(reference)
|
45
|
+
obj = reference.object
|
46
|
+
if obj
|
47
|
+
@lock.synchronize do
|
48
|
+
@references[reference.referenced_object_id] = reference
|
49
|
+
end
|
50
|
+
ObjectSpace.define_finalizer(obj, @finalizer)
|
51
|
+
else
|
52
|
+
push(reference)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add a reference to the queue.
|
57
|
+
def push(reference)
|
58
|
+
if reference
|
59
|
+
@lock.synchronize do
|
60
|
+
@queue.push(reference)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Pull the last reference off the queue. Returns +nil+ if their are no references.
|
66
|
+
def pop
|
67
|
+
@lock.synchronize do
|
68
|
+
@queue.pop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Pull the next reference off the queue. Returns +nil+ if there are no references.
|
73
|
+
def shift
|
74
|
+
@lock.synchronize do
|
75
|
+
@queue.shift
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return +true+ if the queue is empty.
|
80
|
+
def empty?
|
81
|
+
@queue.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the current size of the queue.
|
85
|
+
def size
|
86
|
+
@queue.size
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
begin
|
2
|
+
require 'thread'
|
3
|
+
rescue LoadError
|
4
|
+
# Threads not available. Monitor will do nothing.
|
5
|
+
end
|
6
|
+
|
7
|
+
module Ref
|
8
|
+
# The Monitor class in Ruby 1.8 has some bugs and also threads may not be available on all
|
9
|
+
# runtimes. This class provides a simple, safe re-entrant mutex as an alternative.
|
10
|
+
class SafeMonitor
|
11
|
+
def initialize
|
12
|
+
@owner = nil
|
13
|
+
@count = 0
|
14
|
+
@mutex = defined?(Mutex) ? Mutex.new : nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Acquire an exclusive lock.
|
18
|
+
def lock
|
19
|
+
if @mutex
|
20
|
+
if @owner != Thread.current.object_id
|
21
|
+
@mutex.lock
|
22
|
+
@owner = Thread.current.object_id
|
23
|
+
end
|
24
|
+
@count += 1
|
25
|
+
end
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Release the exclusive lock.
|
30
|
+
def unlock
|
31
|
+
if @mutex
|
32
|
+
if @owner == Thread.current.object_id
|
33
|
+
@count -= 1
|
34
|
+
if @count == 0
|
35
|
+
@owner = nil
|
36
|
+
@mutex.unlock
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Run a block of code with an exclusive lock.
|
43
|
+
def synchronize
|
44
|
+
lock
|
45
|
+
yield
|
46
|
+
ensure
|
47
|
+
unlock
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ref
|
2
|
+
# Implementation of a map in which only softly referenced keys are kept to the map values.
|
3
|
+
# This allows the garbage collector to reclaim these objects if the only reference to them
|
4
|
+
# is the soft reference in the map.
|
5
|
+
#
|
6
|
+
# This is often useful for cache implementations since the map can be allowed to grow
|
7
|
+
# without bound and the garbage collector can be relied on to clean it up as necessary.
|
8
|
+
# One must be careful, though, when accessing entries since they can be collected at
|
9
|
+
# any time until there is a strong reference to the key.
|
10
|
+
#
|
11
|
+
# === Example usage:
|
12
|
+
#
|
13
|
+
# cache = References::SoftKeyMap.new
|
14
|
+
# obj = MyObject.find_by_whatever
|
15
|
+
# obj_info = Service.lookup_object_info(obj)
|
16
|
+
# cache[obj] = Service.lookup_object_info(obj)
|
17
|
+
# cache[obj] # The values looked up from the service
|
18
|
+
# obj = nil
|
19
|
+
# ObjectSpace.garbage_collect
|
20
|
+
# cache.keys # empty array since the keys and values have been reclaimed
|
21
|
+
#
|
22
|
+
# See AbstractReferenceKeyMap for details.
|
23
|
+
class SoftKeyMap < AbstractReferenceKeyMap
|
24
|
+
self.reference_class = SoftReference
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Ref
|
2
|
+
# A SoftReference represents a reference to an object that is not seen by
|
3
|
+
# the tracing phase of the garbage collector. This allows the referenced
|
4
|
+
# object to be garbage collected as if nothing is referring to it.
|
5
|
+
#
|
6
|
+
# A SoftReference differs from a WeakReference in that the garbage collector
|
7
|
+
# is not so eager to reclaim soft references so they should persist longer.
|
8
|
+
#
|
9
|
+
# === Example usage:
|
10
|
+
#
|
11
|
+
# foo = Object.new
|
12
|
+
# ref = References::SoftReference.new(foo)
|
13
|
+
# ref.object # should be foo
|
14
|
+
# ObjectSpace.garbage_collect
|
15
|
+
# ref.object # should be foo
|
16
|
+
# ObjectSpace.garbage_collect
|
17
|
+
# ObjectSpace.garbage_collect
|
18
|
+
# ref.object # should be nil
|
19
|
+
class SoftReference < Reference
|
20
|
+
@@strong_references = [{}]
|
21
|
+
@@gc_flag_set = false
|
22
|
+
|
23
|
+
# Number of garbage collection cycles after an object is used before a reference to it can be reclaimed.
|
24
|
+
MIN_GC_CYCLES = 10
|
25
|
+
|
26
|
+
@@lock = SafeMonitor.new
|
27
|
+
|
28
|
+
@@finalizer = lambda do |object_id|
|
29
|
+
@@lock.synchronize do
|
30
|
+
while @@strong_references.size >= MIN_GC_CYCLES do
|
31
|
+
@@strong_references.shift
|
32
|
+
end
|
33
|
+
@@strong_references.push({}) if @@strong_references.size < MIN_GC_CYCLES
|
34
|
+
@@gc_flag_set = false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create a new soft reference to an object.
|
39
|
+
def initialize(obj)
|
40
|
+
@referenced_object_id = obj.__id__
|
41
|
+
@weak_reference = WeakReference.new(obj)
|
42
|
+
add_strong_reference(obj)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the referenced object. If the object has been reclaimed by the
|
46
|
+
# garbage collector, then this will return nil.
|
47
|
+
def object
|
48
|
+
obj = @weak_reference.object
|
49
|
+
# add a temporary strong reference each time the object is referenced.
|
50
|
+
add_strong_reference(obj) if obj
|
51
|
+
obj
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
# Create a strong reference to the object. This reference will live
|
56
|
+
# for three passes of the garbage collector.
|
57
|
+
def add_strong_reference(obj) #:nodoc:
|
58
|
+
@@lock.synchronize do
|
59
|
+
@@strong_references.last[obj] = true
|
60
|
+
unless @@gc_flag_set
|
61
|
+
@@gc_flag_set = true
|
62
|
+
ObjectSpace.define_finalizer(Object.new, @@finalizer)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ref
|
2
|
+
# Implementation of a map in which soft references are kept to the map values.
|
3
|
+
# This allows the garbage collector to reclaim these objects if the
|
4
|
+
# only reference to them is the soft reference in the map.
|
5
|
+
#
|
6
|
+
# This is often useful for cache implementations since the map can be allowed to grow
|
7
|
+
# without bound and the garbage collector can be relied on to clean it up as necessary.
|
8
|
+
# One must be careful, though, when accessing entries since the values can be collected
|
9
|
+
# at any time until there is a strong reference to them.
|
10
|
+
#
|
11
|
+
# === Example usage:
|
12
|
+
#
|
13
|
+
# cache = References::SoftValueMap.new
|
14
|
+
# foo = "foo"
|
15
|
+
# cache["strong"] = foo # add a value with a strong reference
|
16
|
+
# cache["soft"] = "bar" # add a value without a strong reference
|
17
|
+
# cache["strong"] # "foo"
|
18
|
+
# cache["soft"] # "bar"
|
19
|
+
# ObjectSpace.garbage_collect
|
20
|
+
# ObjectSpace.garbage_collect
|
21
|
+
# cache["strong"] # "foo"
|
22
|
+
# cache["soft"] # nil
|
23
|
+
#
|
24
|
+
# See AbstractReferenceValueMap for details.
|
25
|
+
class SoftValueMap < AbstractReferenceValueMap
|
26
|
+
self.reference_class = SoftReference
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ref
|
2
|
+
# This implementation of Reference holds a strong reference to an object. The
|
3
|
+
# referenced object will not be garbage collected as long as the strong reference
|
4
|
+
# exists.
|
5
|
+
class StrongReference < Reference
|
6
|
+
# Create a new strong reference to an object.
|
7
|
+
def initialize(obj)
|
8
|
+
@obj = obj
|
9
|
+
@referenced_object_id = obj.__id__
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get the referenced object.
|
13
|
+
def object
|
14
|
+
@obj
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ref
|
2
|
+
# Implementation of a map in which only weakly referenced keys are kept to the map values.
|
3
|
+
# This allows the garbage collector to reclaim these objects if the only reference to them
|
4
|
+
# is the weak reference in the map.
|
5
|
+
#
|
6
|
+
# This is often useful for cache implementations since the map can be allowed to grow
|
7
|
+
# without bound and the garbage collector can be relied on to clean it up as necessary.
|
8
|
+
# One must be careful, though, when accessing entries since they can be collected at
|
9
|
+
# any time until there is a strong reference to the key.
|
10
|
+
#
|
11
|
+
# === Example usage:
|
12
|
+
#
|
13
|
+
# cache = References::WeakKeyMap.new
|
14
|
+
# obj = MyObject.find_by_whatever
|
15
|
+
# obj_info = Service.lookup_object_info(obj)
|
16
|
+
# cache[obj] = Service.lookup_object_info(obj)
|
17
|
+
# cache[obj] # The values looked up from the service
|
18
|
+
# obj = nil
|
19
|
+
# ObjectSpace.garbage_collect
|
20
|
+
# cache.keys # empty array since the keys and values have been reclaimed
|
21
|
+
#
|
22
|
+
# See AbstractReferenceKeyMap for details.
|
23
|
+
class WeakKeyMap < AbstractReferenceKeyMap
|
24
|
+
self.reference_class = WeakReference
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ref
|
2
|
+
# A WeakReference represents a reference to an object that is not seen by
|
3
|
+
# the tracing phase of the garbage collector. This allows the referenced
|
4
|
+
# object to be garbage collected as if nothing is referring to it.
|
5
|
+
#
|
6
|
+
# === Example usage:
|
7
|
+
#
|
8
|
+
# foo = Object.new
|
9
|
+
# ref = References::WeakReference.new(foo)
|
10
|
+
# ref.object # should be foo
|
11
|
+
# ObjectSpace.garbage_collect
|
12
|
+
# ref.object # should be nil
|
13
|
+
class WeakReference < Reference
|
14
|
+
|
15
|
+
# Create a weak reference to an object.
|
16
|
+
def initialize(obj)
|
17
|
+
raise NotImplementedError.new("This is an abstract class; you must require an implementation")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get the referenced object. If the object has been reclaimed by the
|
21
|
+
# garbage collector, then this will return nil.
|
22
|
+
def object
|
23
|
+
raise NotImplementedError.new("This is an abstract class; you must require an implementation")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Ref
|
2
|
+
class WeakReference < Reference
|
3
|
+
# This implementation of a weak reference wraps the System::WeakReference class
|
4
|
+
# that comes with IronRuby.
|
5
|
+
def initialize(obj) #:nodoc:
|
6
|
+
@referenced_object_id = obj.__id__
|
7
|
+
@ref = ::System::WeakReference.new(obj)
|
8
|
+
end
|
9
|
+
|
10
|
+
def object #:nodoc:
|
11
|
+
@ref.target
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Ref
|
2
|
+
# This is a pure ruby implementation of a weak reference. It is much more
|
3
|
+
# efficient than the bundled WeakRef implementation because it does not
|
4
|
+
# subclass Delegator which is very heavy to instantiate and utilizes a
|
5
|
+
# fair amount of memory under Ruby 1.8.
|
6
|
+
class WeakReference < Reference
|
7
|
+
|
8
|
+
class ReferencePointer
|
9
|
+
def initialize(object)
|
10
|
+
@referenced_object_id = object.__id__
|
11
|
+
add_backreference(object)
|
12
|
+
end
|
13
|
+
|
14
|
+
def cleanup
|
15
|
+
obj = ObjectSpace._id2ref(@referenced_object_id) rescue nil
|
16
|
+
remove_backreference(obj) if obj
|
17
|
+
end
|
18
|
+
|
19
|
+
def object
|
20
|
+
obj = ObjectSpace._id2ref(@referenced_object_id)
|
21
|
+
obj if verify_backreferences(obj)
|
22
|
+
rescue RangeError
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
# Verify that the object is the same one originally set for the weak reference.
|
28
|
+
def verify_backreferences(obj) #:nodoc:
|
29
|
+
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
|
30
|
+
backreferences && backreferences.include?(object_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add a backreference to the object.
|
34
|
+
def add_backreference(obj) #:nodoc:
|
35
|
+
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
|
36
|
+
unless backreferences
|
37
|
+
backreferences = []
|
38
|
+
obj.instance_variable_set(:@__weak_backreferences__, backreferences)
|
39
|
+
end
|
40
|
+
backreferences << object_id
|
41
|
+
end
|
42
|
+
|
43
|
+
# Remove backreferences from the object.
|
44
|
+
def remove_backreference(obj) #:nodoc:
|
45
|
+
backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
|
46
|
+
if backreferences
|
47
|
+
backreferences.dup.delete(object_id)
|
48
|
+
obj.send(:remove_instance_variable, :@__weak_backreferences__) if backreferences.empty?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@@weak_references = {}
|
54
|
+
@@lock = SafeMonitor.new
|
55
|
+
|
56
|
+
# Finalizer that cleans up weak references when references are destroyed.
|
57
|
+
@@reference_finalizer = lambda do |object_id|
|
58
|
+
@@lock.synchronize do
|
59
|
+
reference_pointer = @@weak_references.delete(object_id)
|
60
|
+
reference_pointer.cleanup if reference_pointer
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create a new weak reference to an object. The existence of the weak reference
|
65
|
+
# will not prevent the garbage collector from reclaiming the referenced object.
|
66
|
+
def initialize(obj) #:nodoc:
|
67
|
+
@referenced_object_id = obj.__id__
|
68
|
+
@@lock.synchronize do
|
69
|
+
@reference_pointer = ReferencePointer.new(obj)
|
70
|
+
@@weak_references[self.object_id] = @reference_pointer
|
71
|
+
end
|
72
|
+
ObjectSpace.define_finalizer(self, @@reference_finalizer)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get the reference object. If the object has already been garbage collected,
|
76
|
+
# then this method will return nil.
|
77
|
+
def object #:nodoc:
|
78
|
+
if @reference_pointer
|
79
|
+
obj = @reference_pointer.object
|
80
|
+
unless obj
|
81
|
+
@@lock.synchronize do
|
82
|
+
@@weak_references.delete(object_id)
|
83
|
+
@reference_pointer.cleanup
|
84
|
+
@reference_pointer = nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
obj
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|