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.
Files changed (45) hide show
  1. data/README.rdoc +35 -0
  2. data/VERSION +1 -0
  3. data/ext/java/org/jruby/ext/ref/ReferencesService.java +28 -0
  4. data/ext/java/org/jruby/ext/ref/RubySoftReference.java +43 -0
  5. data/ext/java/org/jruby/ext/ref/RubyWeakReference.java +43 -0
  6. data/lib/org/jruby/ext/ref/references.jar +0 -0
  7. data/lib/ref.rb +39 -0
  8. data/lib/ref/abstract_reference_key_map.rb +117 -0
  9. data/lib/ref/abstract_reference_value_map.rb +127 -0
  10. data/lib/ref/mock.rb +145 -0
  11. data/lib/ref/reference.rb +24 -0
  12. data/lib/ref/reference_queue.rb +89 -0
  13. data/lib/ref/safe_monitor.rb +50 -0
  14. data/lib/ref/soft_key_map.rb +26 -0
  15. data/lib/ref/soft_reference.rb +67 -0
  16. data/lib/ref/soft_value_map.rb +28 -0
  17. data/lib/ref/strong_reference.rb +17 -0
  18. data/lib/ref/weak_key_map.rb +26 -0
  19. data/lib/ref/weak_reference.rb +26 -0
  20. data/lib/ref/weak_reference/iron_ruby.rb +14 -0
  21. data/lib/ref/weak_reference/pure_ruby.rb +91 -0
  22. data/lib/ref/weak_reference/weak_ref.rb +23 -0
  23. data/lib/ref/weak_value_map.rb +27 -0
  24. data/test/reference_key_map_behavior.rb +157 -0
  25. data/test/reference_key_map_behavior.rbc +4296 -0
  26. data/test/reference_queue_test.rb +60 -0
  27. data/test/reference_queue_test.rbc +1954 -0
  28. data/test/reference_value_map_behavior.rb +137 -0
  29. data/test/reference_value_map_behavior.rbc +3615 -0
  30. data/test/soft_key_map_test.rb +13 -0
  31. data/test/soft_key_map_test.rbc +374 -0
  32. data/test/soft_reference_test.rb +49 -0
  33. data/test/soft_reference_test.rbc +1481 -0
  34. data/test/soft_value_map_test.rb +13 -0
  35. data/test/soft_value_map_test.rbc +374 -0
  36. data/test/strong_reference_test.rb +15 -0
  37. data/test/strong_reference_test.rbc +562 -0
  38. data/test/test_helper.rb +4 -0
  39. data/test/weak_key_map_test.rb +13 -0
  40. data/test/weak_key_map_test.rbc +374 -0
  41. data/test/weak_reference_test.rb +46 -0
  42. data/test/weak_reference_test.rbc +1254 -0
  43. data/test/weak_value_map_test.rb +13 -0
  44. data/test/weak_value_map_test.rbc +374 -0
  45. 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