ref 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -0
  3. data/lib/ref.rb +28 -29
  4. data/lib/ref/abstract_reference_key_map.rb +72 -28
  5. data/lib/ref/abstract_reference_value_map.rb +61 -18
  6. data/lib/ref/reference_queue.rb +8 -8
  7. data/lib/ref/soft_reference.rb +7 -7
  8. data/lib/ref/version.rb +3 -0
  9. data/lib/ref/weak_reference/pure_ruby.rb +9 -9
  10. data/lib/ref_ext.jar +0 -0
  11. metadata +23 -49
  12. data/README.rdoc +0 -40
  13. data/VERSION +0 -1
  14. data/lib/org/jruby/ext/ref/references.jar +0 -0
  15. data/lib/ref/mock.rb +0 -150
  16. data/lib/ref/safe_monitor.rb +0 -50
  17. data/lib/ref/weak_reference/iron_ruby.rb +0 -14
  18. data/test/mock_test.rb +0 -33
  19. data/test/mock_test.rbc +0 -856
  20. data/test/reference_key_map_behavior.rb +0 -155
  21. data/test/reference_key_map_behavior.rbc +0 -4241
  22. data/test/reference_queue_test.rb +0 -60
  23. data/test/reference_queue_test.rbc +0 -1938
  24. data/test/reference_value_map_behavior.rb +0 -135
  25. data/test/reference_value_map_behavior.rbc +0 -3560
  26. data/test/soft_key_map_test.rb +0 -13
  27. data/test/soft_key_map_test.rbc +0 -331
  28. data/test/soft_reference_test.rb +0 -49
  29. data/test/soft_reference_test.rbc +0 -1481
  30. data/test/soft_value_map_test.rb +0 -13
  31. data/test/soft_value_map_test.rbc +0 -331
  32. data/test/strong_reference_test.rb +0 -15
  33. data/test/strong_reference_test.rbc +0 -546
  34. data/test/test_helper.rb +0 -4
  35. data/test/test_helper.rbc +0 -143
  36. data/test/weak_key_map_test.rb +0 -13
  37. data/test/weak_key_map_test.rbc +0 -331
  38. data/test/weak_reference_test.rb +0 -54
  39. data/test/weak_reference_test.rbc +0 -1510
  40. data/test/weak_value_map_test.rb +0 -13
  41. data/test/weak_value_map_test.rbc +0 -331
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6eb5f8b9c225f1e9ebac1aaa62ef9f3984cfe14f
4
- data.tar.gz: a19284185151618e6bcb5f33cb7abe336b87ae42
3
+ metadata.gz: 65d0ceb85d4d1208144f2ad51c92269c4f0f689f
4
+ data.tar.gz: 7ee73e669c4d7532ed44743ea17f4acbd2d983fd
5
5
  SHA512:
6
- metadata.gz: 7a4f349540019ab696d4ecdacf589e523804daecc90ff3b3798f3d9b9382efe5dedf14c4634d6c8203672911027da17cd1d052cc54e5bf76cfd7a3ba6b63f9ea
7
- data.tar.gz: 3ccad9fcd17a871de36d032a57f130deca6df40605c8798bcf86bdcb204cdb40376eba445100772f5636acddffe72f3c00cd8bee5c7e0a796ea3b5602f062cba
6
+ metadata.gz: 22589cec63a2275a3a4f71e903e858d5fe33180a866ac3108c8d4768b08b8f1998dc85e32f8f3a81ce5647b7265e9b4a24a1c22d80ed5368e2e1c6d76167a154
7
+ data.tar.gz: db38d2ca1d6a8fbe96a2fcb8c6d1308226648928d95c6cf62ece3832fccb7bf7e9241d13f1f87d0d55c1b8cac7715fdb361dc1d50fbdf5351aa896244c78d690
@@ -0,0 +1,41 @@
1
+ # Ref
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ref.svg)](http://badge.fury.io/rb/ref) [![Build Status](https://travis-ci.org/ruby-concurrency/ref.svg?branch=master)](https://travis-ci.org/ruby-concurrency/ref) [![Coverage Status](https://img.shields.io/coveralls/ruby-concurrency/ref/master.svg)](https://coveralls.io/r/ruby-concurrency/ref) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/ref.svg)](https://codeclimate.com/github/ruby-concurrency/ref) [![Dependency Status](https://gemnasium.com/ruby-concurrency/ref.svg)](https://gemnasium.com/ruby-concurrency/ref) [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby)
4
+
5
+ This library provides object references for Ruby as well as some common utilities for working with references. Object references are used to point to other objects and come in three distinct flavors that interact differently with the garbage collector.
6
+
7
+ * `Ref::StrongReference` - This is a plain old pointer to another object.
8
+ * `Ref::WeakReference` - This is a pointer to another object, but it is not seen by the garbage collector and the memory used by the object can be reclaimed at any time.
9
+ * `Ref::SoftReference` - This is similar to a weak reference, but the garbage collector is not as eager to reclaim the referenced object.
10
+
11
+ All of these classes extend from a common `Ref::Reference` class and have a common interface.
12
+
13
+ Weak and soft references are useful when you have instantiated objects that you may want to use again but can recreate if necessary. Since the garbage collector determines when to reclaim the memory used by the objects, you don't need to worry about bloating the Ruby heap.
14
+
15
+ ## Example Usage
16
+
17
+ ```ruby
18
+ ref = Ref::WeakReference.new("hello")
19
+ ref.object # should be "hello"
20
+ ObjectSpace.garbage_collect
21
+ ref.object # should be nil (assuming the garbage collector reclaimed the reference)
22
+ ```
23
+
24
+ ## Goodies
25
+
26
+ This library also includes tools for some common uses of weak and soft references.
27
+
28
+ * `Ref::WeakKeyMap` - A map of keys to values where the keys are weak references
29
+ * `Ref::WeakValueMap` - A map of keys to values where the values are weak references
30
+ * `Ref::SoftKeyMap` - A map of keys to values where the keys are soft references
31
+ * `Ref::SoftValueMap` - A map of keys to values where the values are soft references
32
+ * `Ref::ReferenceQueue` - A thread safe implementation of a queue that will add references to itself as their objects are garbage collected.
33
+
34
+ ## Problems with WeakRef
35
+
36
+ Ruby does come with the `WeakRef` class in the standard library. However, there are [issues with this class](https://bugs.ruby-lang.org/issues/4168) across several different Ruby runtimes. This gem provides a common interface to weak references that works across MRI, Ruby Enterprise Edition, YARV, JRuby and Rubinius.
37
+
38
+ 1. Rubinius - Rubinius implements `WeakRef` with a lighter weight version of delegation and works very well.
39
+ 2. YARV 1.9 - `WeakRef` is unsafe to use because the garbage collector can run in a different system thread than a thread allocating memory. This exposes a bug where a `WeakRef` may end up pointing to a completely different object than it originally referenced.
40
+ 3. MRI Ruby 2.0+ has a good implementation of `WeakRef`.
41
+
data/lib/ref.rb CHANGED
@@ -1,45 +1,44 @@
1
1
  module Ref
2
- require File.join(File.dirname(__FILE__), "ref", "abstract_reference_value_map.rb")
3
- require File.join(File.dirname(__FILE__), "ref", "abstract_reference_key_map.rb")
4
- require File.join(File.dirname(__FILE__), "ref", "reference.rb")
5
- require File.join(File.dirname(__FILE__), "ref", "reference_queue.rb")
6
- require File.join(File.dirname(__FILE__), "ref", "safe_monitor.rb")
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
3
 
8
- # Set the best implementation for weak references based on the runtime.
9
- if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
10
- # Use native Java references
4
+ require 'ref/abstract_reference_value_map'
5
+ require 'ref/abstract_reference_key_map'
6
+ require 'ref/reference'
7
+ require 'ref/reference_queue'
8
+
9
+ if defined?(Java)
11
10
  begin
12
- $LOAD_PATH.unshift(File.dirname(__FILE__))
11
+ require 'ref_ext'
13
12
  require 'org/jruby/ext/ref/references'
14
- ensure
15
- $LOAD_PATH.shift if $LOAD_PATH.first == File.dirname(__FILE__)
13
+ rescue LoadError
14
+ require 'ref/soft_reference'
15
+ require 'ref/weak_reference'
16
+ warn 'Error loading Rspec rake tasks, probably building the gem...'
16
17
  end
17
18
  else
18
- require File.join(File.dirname(__FILE__), "ref", "soft_reference.rb")
19
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
20
- # IronRuby has it's own implementation of weak references.
21
- require File.join(File.dirname(__FILE__), "ref", "weak_reference", "iron_ruby.rb")
22
- elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
19
+ require 'ref/soft_reference'
20
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
23
21
  # If using Rubinius set the implementation to use WeakRef since it is very efficient and using finalizers is not.
24
- require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
22
+ require 'ref/weak_reference/weak_ref'
25
23
  elsif defined?(::ObjectSpace::WeakMap)
26
24
  # Ruby 2.0 has a working implementation of weakref.rb backed by the new ObjectSpace::WeakMap
27
- require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
25
+ require 'ref/weak_reference/weak_ref'
28
26
  elsif defined?(::ObjectSpace._id2ref)
29
27
  # If ObjectSpace can lookup objects from their object_id, then use the pure ruby implementation.
30
- require File.join(File.dirname(__FILE__), "ref", "weak_reference", "pure_ruby.rb")
28
+ require 'ref/weak_reference/pure_ruby'
31
29
  else
32
30
  # Otherwise, wrap the standard library WeakRef class
33
- require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
31
+ require 'ref/weak_reference/weak_ref'
34
32
  end
35
33
  end
36
-
37
- require File.join(File.dirname(__FILE__), "ref", "soft_key_map.rb")
38
- require File.join(File.dirname(__FILE__), "ref", "soft_value_map.rb")
39
- require File.join(File.dirname(__FILE__), "ref", "strong_reference.rb")
40
- require File.join(File.dirname(__FILE__), "ref", "weak_key_map.rb")
41
- require File.join(File.dirname(__FILE__), "ref", "weak_value_map.rb")
42
-
43
- # Used for testing
44
- autoload :Mock, File.join(File.dirname(__FILE__), "ref", "mock.rb")
34
+
35
+ require 'ref/soft_key_map'
36
+ require 'ref/soft_value_map'
37
+ require 'ref/strong_reference'
38
+ require 'ref/weak_key_map'
39
+ require 'ref/weak_value_map'
40
+
41
+ def self.jruby?
42
+ defined?(Java)
43
+ end
45
44
  end
@@ -9,29 +9,33 @@ module Ref
9
9
  def reference_class=(klass) #:nodoc:
10
10
  @reference_class = klass
11
11
  end
12
-
12
+
13
13
  def reference_class #:nodoc:
14
14
  raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
15
15
  @reference_class
16
16
  end
17
17
  end
18
-
18
+
19
19
  # Create a new map. Values added to the hash will be cleaned up by the garbage
20
20
  # collector if there are no other reference except in the map.
21
21
  def initialize
22
22
  @values = {}
23
23
  @references_to_keys_map = {}
24
- @lock = SafeMonitor.new
24
+ @lock = Monitor.new
25
25
  @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
26
26
  end
27
27
 
28
28
  # Get a value from the map by key. If the value has been reclaimed by the garbage
29
29
  # collector, this will return nil.
30
30
  def [](key)
31
- rkey = ref_key(key)
32
- @values[rkey] if rkey
31
+ @lock.synchronize do
32
+ rkey = ref_key(key)
33
+ @values[rkey] if rkey
34
+ end
33
35
  end
34
36
 
37
+ alias_method :get, :[]
38
+
35
39
  # Add a key/value to the map.
36
40
  def []=(key, value)
37
41
  ObjectSpace.define_finalizer(key, @reference_cleanup)
@@ -41,14 +45,18 @@ module Ref
41
45
  end
42
46
  end
43
47
 
48
+ alias_method :put, :[]=
49
+
44
50
  # Remove the value associated with the key from the map.
45
51
  def delete(key)
46
- rkey = ref_key(key)
47
- if rkey
48
- @references_to_keys_map.delete(rkey)
49
- @values.delete(rkey)
50
- else
51
- nil
52
+ @lock.synchronize do
53
+ rkey = ref_key(key)
54
+ if rkey
55
+ @references_to_keys_map.delete(rkey)
56
+ @values.delete(rkey)
57
+ else
58
+ nil
59
+ end
52
60
  end
53
61
  end
54
62
 
@@ -56,14 +64,21 @@ module Ref
56
64
  def keys
57
65
  @values.keys.collect{|rkey| @references_to_keys_map[rkey].object}.compact
58
66
  end
59
-
67
+
60
68
  # Turn the map into an arry of [key, value] entries.
61
69
  def to_a
62
70
  array = []
63
71
  each{|k,v| array << [k, v]}
64
72
  array
65
73
  end
66
-
74
+
75
+ # Returns a hash containing the names and values for the struct’s members.
76
+ def to_h
77
+ hash = {}
78
+ each{|k,v| hash[k] = v}
79
+ hash
80
+ end
81
+
67
82
  # Iterate through all the key/value pairs in the map that have not been reclaimed
68
83
  # by the garbage collector.
69
84
  def each
@@ -81,13 +96,42 @@ module Ref
81
96
  end
82
97
  end
83
98
 
99
+ # Returns a new struct containing the contents of `other` and the contents
100
+ # of `self`. If no block is specified, the value for entries with duplicate
101
+ # keys will be that of `other`. Otherwise the value for each duplicate key
102
+ # is determined by calling the block with the key, its value in `self` and
103
+ # its value in `other`.
104
+ def merge(other_hash, &block)
105
+ to_h.merge(other_hash, &block).reduce(self.class.new) do |map, pair|
106
+ map[pair.first] = pair.last
107
+ map
108
+ end
109
+ end
110
+
84
111
  # Merge the values from another hash into this map.
85
112
  def merge!(other_hash)
86
- other_hash.each do |key, value|
87
- self[key] = value
113
+ @lock.synchronize do
114
+ other_hash.each { |key, value| self[key] = value }
88
115
  end
89
116
  end
90
117
 
118
+ # The number of entries in the map
119
+ def size
120
+ @references_to_keys_map.count do |_, ref|
121
+ ref.object
122
+ end
123
+ end
124
+
125
+ alias_method :length, :size
126
+
127
+ # True if there are entries that exist in the map
128
+ def empty?
129
+ @references_to_keys_map.each do |_, ref|
130
+ return false if ref.object
131
+ end
132
+ true
133
+ end
134
+
91
135
  def inspect
92
136
  live_entries = {}
93
137
  each do |key, value|
@@ -98,20 +142,20 @@ module Ref
98
142
 
99
143
  private
100
144
 
101
- def ref_key (key)
102
- ref = @references_to_keys_map[key.__id__]
103
- if ref && ref.object
104
- ref.referenced_object_id
105
- else
106
- nil
107
- end
145
+ def ref_key (key)
146
+ ref = @references_to_keys_map[key.__id__]
147
+ if ref && ref.object
148
+ ref.referenced_object_id
149
+ else
150
+ nil
108
151
  end
109
-
110
- def remove_reference_to(object_id)
111
- @lock.synchronize do
112
- @references_to_keys_map.delete(object_id)
113
- @values.delete(object_id)
114
- end
152
+ end
153
+
154
+ def remove_reference_to(object_id)
155
+ @lock.synchronize do
156
+ @references_to_keys_map.delete(object_id)
157
+ @values.delete(object_id)
115
158
  end
159
+ end
116
160
  end
117
161
  end
@@ -9,30 +9,34 @@ module Ref
9
9
  def reference_class=(klass) #:nodoc:
10
10
  @reference_class = klass
11
11
  end
12
-
12
+
13
13
  def reference_class #:nodoc:
14
14
  raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
15
15
  @reference_class
16
16
  end
17
17
  end
18
-
18
+
19
19
  # Create a new map. Values added to the map will be cleaned up by the garbage
20
20
  # collector if there are no other reference except in the map.
21
21
  def initialize
22
22
  @references = {}
23
23
  @references_to_keys_map = {}
24
- @lock = SafeMonitor.new
24
+ @lock = Monitor.new
25
25
  @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
26
26
  end
27
27
 
28
28
  # Get a value from the map by key. If the value has been reclaimed by the garbage
29
29
  # collector, this will return nil.
30
30
  def [](key)
31
- ref = @references[key]
32
- value = ref.object if ref
33
- value
31
+ @lock.synchronize do
32
+ ref = @references[key]
33
+ value = ref.object if ref
34
+ value
35
+ end
34
36
  end
35
37
 
38
+ alias_method :get, :[]
39
+
36
40
  # Add a key/value to the map.
37
41
  def []=(key, value)
38
42
  ObjectSpace.define_finalizer(value, @reference_cleanup)
@@ -49,6 +53,8 @@ module Ref
49
53
  value
50
54
  end
51
55
 
56
+ alias_method :put, :[]=
57
+
52
58
  # Remove the entry associated with the key from the map.
53
59
  def delete(key)
54
60
  ref = @references.delete(key)
@@ -70,14 +76,21 @@ module Ref
70
76
  each{|k,v| vals << v}
71
77
  vals
72
78
  end
73
-
79
+
74
80
  # Turn the map into an arry of [key, value] entries
75
81
  def to_a
76
82
  array = []
77
83
  each{|k,v| array << [k, v]}
78
84
  array
79
85
  end
80
-
86
+
87
+ # Returns a hash containing the names and values for the struct’s members.
88
+ def to_h
89
+ hash = {}
90
+ each{|k,v| hash[k] = v}
91
+ hash
92
+ end
93
+
81
94
  # Iterate through all the key/value pairs in the map that have not been reclaimed
82
95
  # by the garbage collector.
83
96
  def each
@@ -95,13 +108,43 @@ module Ref
95
108
  end
96
109
  end
97
110
 
111
+ # Returns a new struct containing the contents of `other` and the contents
112
+ # of `self`. If no block is specified, the value for entries with duplicate
113
+ # keys will be that of `other`. Otherwise the value for each duplicate key
114
+ # is determined by calling the block with the key, its value in `self` and
115
+ # its value in `other`.
116
+ def merge(other_hash, &block)
117
+ to_h.merge(other_hash, &block).reduce(self.class.new) do |map, pair|
118
+ map[pair.first] = pair.last
119
+ map
120
+ end
121
+ end
122
+
98
123
  # Merge the values from another hash into this map.
99
124
  def merge!(other_hash)
100
- other_hash.each do |key, value|
101
- self[key] = value
125
+ @lock.synchronize do
126
+ other_hash.each { |key, value| self[key] = value }
102
127
  end
103
128
  end
104
129
 
130
+ # The number of entries in the map
131
+ def size
132
+ @references.count do |_, ref|
133
+ ref.object
134
+ end
135
+ end
136
+
137
+ alias_method :length, :size
138
+
139
+ # True if there are entries that exist in the map
140
+ def empty?
141
+ @references.each do |_, ref|
142
+ return false if ref.object
143
+ end
144
+
145
+ true
146
+ end
147
+
105
148
  def inspect
106
149
  live_entries = {}
107
150
  each do |key, value|
@@ -112,16 +155,16 @@ module Ref
112
155
 
113
156
  private
114
157
 
115
- def remove_reference_to(object_id)
116
- @lock.synchronize do
117
- keys = @references_to_keys_map[object_id]
118
- if keys
119
- keys.each do |key|
120
- @references.delete(key)
121
- end
122
- @references_to_keys_map.delete(object_id)
158
+ def remove_reference_to(object_id)
159
+ @lock.synchronize do
160
+ keys = @references_to_keys_map[object_id]
161
+ if keys
162
+ keys.each do |key|
163
+ @references.delete(key)
123
164
  end
165
+ @references_to_keys_map.delete(object_id)
124
166
  end
125
167
  end
168
+ end
126
169
  end
127
170
  end
@@ -18,7 +18,7 @@ module Ref
18
18
  # # Do something...
19
19
  # end
20
20
  # end
21
- #
21
+ #
22
22
  # queue = Ref::ReferenceQueue.new
23
23
  # ref = MyRef.new(Object.new)
24
24
  # queue.monitor(ref)
@@ -30,7 +30,7 @@ module Ref
30
30
  def initialize
31
31
  @queue = []
32
32
  @references = {}
33
- @lock = SafeMonitor.new
33
+ @lock = Monitor.new
34
34
  @finalizer = lambda do |object_id|
35
35
  @lock.synchronize do
36
36
  ref = @references.delete(object_id)
@@ -38,7 +38,7 @@ module Ref
38
38
  end
39
39
  end
40
40
  end
41
-
41
+
42
42
  # Monitor a reference. When the object the reference points to is garbage collected,
43
43
  # the reference will be added to the queue.
44
44
  def monitor(reference)
@@ -52,7 +52,7 @@ module Ref
52
52
  push(reference)
53
53
  end
54
54
  end
55
-
55
+
56
56
  # Add a reference to the queue.
57
57
  def push(reference)
58
58
  if reference
@@ -61,26 +61,26 @@ module Ref
61
61
  end
62
62
  end
63
63
  end
64
-
64
+
65
65
  # Pull the last reference off the queue. Returns +nil+ if their are no references.
66
66
  def pop
67
67
  @lock.synchronize do
68
68
  @queue.pop
69
69
  end
70
70
  end
71
-
71
+
72
72
  # Pull the next reference off the queue. Returns +nil+ if there are no references.
73
73
  def shift
74
74
  @lock.synchronize do
75
75
  @queue.shift
76
76
  end
77
77
  end
78
-
78
+
79
79
  # Return +true+ if the queue is empty.
80
80
  def empty?
81
81
  @queue.empty?
82
82
  end
83
-
83
+
84
84
  # Get the current size of the queue.
85
85
  def size
86
86
  @queue.size