ref 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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