ref 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|