concurrent-ruby 0.8.0 → 0.9.0.pre2

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -2
  3. data/README.md +103 -54
  4. data/lib/concurrent.rb +34 -14
  5. data/lib/concurrent/async.rb +164 -50
  6. data/lib/concurrent/atom.rb +171 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +27 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +25 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +226 -112
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +10 -0
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
  46. data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
  47. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  48. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  49. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
  50. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
  51. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  52. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  53. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  54. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  55. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  56. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  57. data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
  58. data/lib/concurrent/executor/timer_set.rb +96 -84
  59. data/lib/concurrent/executors.rb +1 -1
  60. data/lib/concurrent/future.rb +70 -38
  61. data/lib/concurrent/immutable_struct.rb +89 -0
  62. data/lib/concurrent/ivar.rb +152 -60
  63. data/lib/concurrent/lazy_register.rb +40 -20
  64. data/lib/concurrent/maybe.rb +226 -0
  65. data/lib/concurrent/mutable_struct.rb +227 -0
  66. data/lib/concurrent/mvar.rb +44 -43
  67. data/lib/concurrent/promise.rb +208 -134
  68. data/lib/concurrent/scheduled_task.rb +339 -43
  69. data/lib/concurrent/settable_struct.rb +127 -0
  70. data/lib/concurrent/synchronization.rb +17 -0
  71. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  72. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  73. data/lib/concurrent/synchronization/condition.rb +53 -0
  74. data/lib/concurrent/synchronization/java_object.rb +35 -0
  75. data/lib/concurrent/synchronization/lock.rb +32 -0
  76. data/lib/concurrent/synchronization/monitor_object.rb +24 -0
  77. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  78. data/lib/concurrent/synchronization/object.rb +78 -0
  79. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  80. data/lib/concurrent/timer_task.rb +87 -100
  81. data/lib/concurrent/tvar.rb +42 -38
  82. data/lib/concurrent/utilities.rb +3 -1
  83. data/lib/concurrent/utility/at_exit.rb +97 -0
  84. data/lib/concurrent/utility/engine.rb +40 -0
  85. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  86. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  87. data/lib/concurrent/utility/processor_counter.rb +156 -0
  88. data/lib/concurrent/utility/timeout.rb +18 -14
  89. data/lib/concurrent/utility/timer.rb +11 -6
  90. data/lib/concurrent/version.rb +2 -1
  91. data/lib/concurrent_ruby.rb +1 -0
  92. metadata +47 -83
  93. data/lib/concurrent/actor.rb +0 -103
  94. data/lib/concurrent/actor/behaviour.rb +0 -70
  95. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  96. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  97. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  98. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  99. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  100. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  101. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  102. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  103. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  104. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  105. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  106. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  107. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  108. data/lib/concurrent/actor/context.rb +0 -154
  109. data/lib/concurrent/actor/core.rb +0 -217
  110. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  111. data/lib/concurrent/actor/envelope.rb +0 -41
  112. data/lib/concurrent/actor/errors.rb +0 -27
  113. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  114. data/lib/concurrent/actor/public_delegations.rb +0 -40
  115. data/lib/concurrent/actor/reference.rb +0 -81
  116. data/lib/concurrent/actor/root.rb +0 -37
  117. data/lib/concurrent/actor/type_check.rb +0 -48
  118. data/lib/concurrent/actor/utils.rb +0 -10
  119. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  120. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  121. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  122. data/lib/concurrent/actor/utils/pool.rb +0 -59
  123. data/lib/concurrent/actress.rb +0 -3
  124. data/lib/concurrent/agent.rb +0 -209
  125. data/lib/concurrent/atomic.rb +0 -92
  126. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  127. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  128. data/lib/concurrent/atomic/synchronization.rb +0 -51
  129. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  130. data/lib/concurrent/channel/channel.rb +0 -41
  131. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  132. data/lib/concurrent/channel/waitable_list.rb +0 -40
  133. data/lib/concurrent/channels.rb +0 -5
  134. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  135. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  136. data/lib/concurrent/collections.rb +0 -3
  137. data/lib/concurrent/dereferenceable.rb +0 -108
  138. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  139. data/lib/concurrent/logging.rb +0 -20
  140. data/lib/concurrent/obligation.rb +0 -171
  141. data/lib/concurrent/observable.rb +0 -73
  142. data/lib/concurrent/options_parser.rb +0 -52
  143. data/lib/concurrent/utility/processor_count.rb +0 -152
  144. data/lib/extension_helper.rb +0 -37
@@ -1,8 +1,9 @@
1
- require 'concurrent/atomic'
1
+ require 'concurrent/atomic/thread_local_var/weak_key_map'
2
2
 
3
3
  module Concurrent
4
4
 
5
- # @!macro [attach] abstract_thread_local_var
5
+ # @!macro [attach] thread_local_var
6
+ #
6
7
  # A `ThreadLocalVar` is a variable where the value is different for each thread.
7
8
  # Each variable may have a default value, but when you modify the variable only
8
9
  # the current thread will ever see that change.
@@ -29,64 +30,31 @@ module Concurrent
29
30
  # end
30
31
  #
31
32
  # v.value #=> 14
33
+ #
34
+ # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
35
+ #
36
+ # @!visibility private
32
37
  class AbstractThreadLocalVar
33
38
 
34
- module ThreadLocalRubyStorage
35
-
36
- protected
37
-
38
- unless RUBY_PLATFORM == 'java'
39
- require 'ref'
40
- end
41
-
42
- def allocate_storage
43
- @storage = Ref::WeakKeyMap.new
44
- end
45
-
46
- def get
47
- @storage[Thread.current]
48
- end
49
-
50
- def set(value, &block)
51
- key = Thread.current
52
-
53
- @storage[key] = value
54
-
55
- if block_given?
56
- begin
57
- block.call
58
- ensure
59
- @storage.delete key
60
- end
61
- end
62
- end
63
- end
64
-
65
- module ThreadLocalJavaStorage
66
-
67
- protected
68
-
69
- def allocate_storage
70
- @var = java.lang.ThreadLocal.new
71
- end
72
-
73
- def get
74
- @var.get
75
- end
76
-
77
- def set(value)
78
- @var.set(value)
79
- end
80
-
81
- end
82
-
39
+ # @!visibility private
83
40
  NIL_SENTINEL = Object.new
41
+ private_constant :NIL_SENTINEL
84
42
 
43
+ # @!macro [attach] thread_local_var_method_initialize
44
+ #
45
+ # Creates a thread local variable.
46
+ #
47
+ # @param [Object] default the default value when otherwise unset
85
48
  def initialize(default = nil)
86
49
  @default = default
87
50
  allocate_storage
88
51
  end
89
52
 
53
+ # @!macro [attach] thread_local_var_method_get
54
+ #
55
+ # Returns the value in the current thread's copy of this thread-local variable.
56
+ #
57
+ # @return [Object] the current value
90
58
  def value
91
59
  value = get
92
60
 
@@ -99,10 +67,24 @@ module Concurrent
99
67
  end
100
68
  end
101
69
 
70
+ # @!macro [attach] thread_local_var_method_set
71
+ #
72
+ # Sets the current thread's copy of this thread-local variable to the specified value.
73
+ #
74
+ # @param [Object] value the value to set
75
+ # @return [Object] the new value
102
76
  def value=(value)
103
77
  bind value
104
78
  end
105
79
 
80
+ # @!macro [attach] thread_local_var_method_bind
81
+ #
82
+ # Bind the given value to thread local storage during
83
+ # execution of the given block.
84
+ #
85
+ # @param [Object] value the value to bind
86
+ # @yield the operation to be performed with the bound variable
87
+ # @return [Object] the value
106
88
  def bind(value, &block)
107
89
  if value.nil?
108
90
  stored_value = NIL_SENTINEL
@@ -110,20 +92,110 @@ module Concurrent
110
92
  stored_value = value
111
93
  end
112
94
 
113
- set stored_value, &block
95
+ set(stored_value, &block)
114
96
 
115
97
  value
116
98
  end
117
99
 
100
+ protected
101
+
102
+ # @!visibility private
103
+ def allocate_storage
104
+ raise NotImplementedError
105
+ end
106
+
107
+ # @!visibility private
108
+ def get
109
+ raise NotImplementedError
110
+ end
111
+
112
+ # @!visibility private
113
+ def set(value)
114
+ raise NotImplementedError
115
+ end
118
116
  end
119
117
 
120
- # @!macro abstract_thread_local_var
121
- class ThreadLocalVar < AbstractThreadLocalVar
122
- if RUBY_PLATFORM == 'java'
123
- include ThreadLocalJavaStorage
124
- else
125
- include ThreadLocalRubyStorage
118
+ # @!visibility private
119
+ # @!macro internal_implementation_note
120
+ class RubyThreadLocalVar < AbstractThreadLocalVar
121
+
122
+ protected
123
+
124
+ # @!visibility private
125
+ def allocate_storage
126
+ @storage = WeakKeyMap.new
127
+ end
128
+
129
+ # @!visibility private
130
+ def get
131
+ @storage[Thread.current]
132
+ end
133
+
134
+ # @!visibility private
135
+ def set(value)
136
+ key = Thread.current
137
+
138
+ @storage[key] = value
139
+
140
+ if block_given?
141
+ begin
142
+ yield
143
+ ensure
144
+ @storage.delete(key)
145
+ end
146
+ end
126
147
  end
127
148
  end
128
149
 
150
+ if Concurrent.on_jruby?
151
+
152
+ # @!visibility private
153
+ # @!macro internal_implementation_note
154
+ class JavaThreadLocalVar < AbstractThreadLocalVar
155
+
156
+ protected
157
+
158
+ # @!visibility private
159
+ def allocate_storage
160
+ @var = java.lang.ThreadLocal.new
161
+ end
162
+
163
+ # @!visibility private
164
+ def get
165
+ @var.get
166
+ end
167
+
168
+ # @!visibility private
169
+ def set(value)
170
+ @var.set(value)
171
+ end
172
+ end
173
+ end
174
+
175
+ # @!visibility private
176
+ # @!macro internal_implementation_note
177
+ ThreadLocalVarImplementation = case
178
+ when Concurrent.on_jruby?
179
+ JavaThreadLocalVar
180
+ else
181
+ RubyThreadLocalVar
182
+ end
183
+ private_constant :ThreadLocalVarImplementation
184
+
185
+ # @!macro thread_local_var
186
+ class ThreadLocalVar < ThreadLocalVarImplementation
187
+
188
+ # @!method initialize(default = nil)
189
+ # @!macro thread_local_var_method_initialize
190
+
191
+ # @!method value
192
+ # @!macro thread_local_var_method_get
193
+
194
+ # @!method value=(value)
195
+ # @!macro thread_local_var_method_set
196
+
197
+ # @!method bind(value, &block)
198
+ # @!macro thread_local_var_method_bind
199
+
200
+ end
129
201
  end
@@ -0,0 +1,236 @@
1
+ # Copyright (c) 2013 Brian Durand
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ module Concurrent
23
+
24
+ # @!visibility private
25
+ class AbstractThreadLocalVar
26
+
27
+ begin
28
+ require 'weakref'
29
+
30
+ # @!visibility private
31
+ class WeakReference
32
+
33
+ # The object id of the object being referenced.
34
+ attr_reader :referenced_object_id
35
+
36
+ # This implementation of a weak reference simply wraps the standard WeakRef implementation
37
+ # that comes with the Ruby standard library.
38
+ def initialize(obj)
39
+ @referenced_object_id = obj.__id__
40
+ @ref = ::WeakRef.new(obj)
41
+ end
42
+
43
+ def object
44
+ @ref.__getobj__
45
+ rescue => e
46
+ # Jruby implementation uses RefError while MRI uses WeakRef::RefError
47
+ if (defined?(RefError) && e.is_a?(RefError)) || (defined?(::WeakRef::RefError) && e.is_a?(::WeakRef::RefError))
48
+ nil
49
+ else
50
+ raise e
51
+ end
52
+ end
53
+ end
54
+
55
+ rescue LoadError
56
+
57
+ require 'monitor'
58
+
59
+ # This is a pure ruby implementation of a weak reference. It is much more
60
+ # efficient than the WeakRef implementation bundled in MRI 1.8 and 1.9
61
+ # subclass Delegator which is very heavy to instantiate and utilizes a
62
+ # because it does not fair amount of memory under Ruby 1.8.
63
+ #
64
+ # @!visibility private
65
+ class WeakReference
66
+
67
+ # The object id of the object being referenced.
68
+ attr_reader :referenced_object_id
69
+
70
+ # @!visibility private
71
+ class ReferencePointer
72
+ def initialize(object)
73
+ @referenced_object_id = object.__id__
74
+ add_backreference(object)
75
+ end
76
+
77
+ def cleanup
78
+ obj = ObjectSpace._id2ref(@referenced_object_id) rescue nil
79
+ remove_backreference(obj) if obj
80
+ end
81
+
82
+ def object
83
+ obj = ObjectSpace._id2ref(@referenced_object_id)
84
+ obj if verify_backreferences(obj)
85
+ rescue RangeError
86
+ nil
87
+ end
88
+
89
+ private
90
+
91
+ # Verify that the object is the same one originally set for the weak reference.
92
+ def verify_backreferences(obj)
93
+ return nil unless supports_backreference?(obj)
94
+ backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
95
+ backreferences && backreferences.include?(object_id)
96
+ end
97
+
98
+ # Add a backreference to the object.
99
+ def add_backreference(obj)
100
+ return unless supports_backreference?(obj)
101
+ backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
102
+ unless backreferences
103
+ backreferences = []
104
+ obj.instance_variable_set(:@__weak_backreferences__, backreferences)
105
+ end
106
+ backreferences << object_id
107
+ end
108
+
109
+ # Remove backreferences from the object.
110
+ def remove_backreference(obj)
111
+ return unless supports_backreference?(obj)
112
+ backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__)
113
+ if backreferences
114
+ backreferences.dup.delete(object_id)
115
+ obj.send(:remove_instance_variable, :@__weak_backreferences__) if backreferences.empty?
116
+ end
117
+ end
118
+
119
+ def supports_backreference?(obj)
120
+ obj.respond_to?(:instance_variable_get) && obj.respond_to?(:instance_variable_defined?)
121
+ rescue NoMethodError
122
+ false
123
+ end
124
+ end
125
+
126
+ private_constant :ReferencePointer
127
+
128
+ @@weak_references = {}
129
+ @@lock = Monitor.new
130
+
131
+ # Finalizer that cleans up weak references when references are destroyed.
132
+ @@reference_finalizer = lambda do |object_id|
133
+ @@lock.synchronize do
134
+ reference_pointer = @@weak_references.delete(object_id)
135
+ reference_pointer.cleanup if reference_pointer
136
+ end
137
+ end
138
+
139
+ # Create a new weak reference to an object. The existence of the weak reference
140
+ # will not prevent the garbage collector from reclaiming the referenced object.
141
+ def initialize(obj)
142
+ @referenced_object_id = obj.__id__
143
+ @@lock.synchronize do
144
+ @reference_pointer = ReferencePointer.new(obj)
145
+ @@weak_references[self.object_id] = @reference_pointer
146
+ end
147
+ ObjectSpace.define_finalizer(self, @@reference_finalizer)
148
+ end
149
+
150
+ # Get the reference object. If the object has already been garbage collected,
151
+ # then this method will return nil.
152
+ def object
153
+ if @reference_pointer
154
+ obj = @reference_pointer.object
155
+ unless obj
156
+ @@lock.synchronize do
157
+ @@weak_references.delete(object_id)
158
+ @reference_pointer.cleanup
159
+ @reference_pointer = nil
160
+ end
161
+ end
162
+ obj
163
+ end
164
+ end
165
+ end
166
+
167
+ private_constant :WeakReference
168
+ end
169
+
170
+ # The classes behave similar to Hashes, but the keys in the map are not strong references
171
+ # and can be reclaimed by the garbage collector at any time. When a key is reclaimed, the
172
+ # map entry will be removed.
173
+ #
174
+ # @!visibility private
175
+ class WeakKeyMap
176
+
177
+ # Create a new map. Values added to the hash will be cleaned up by the garbage
178
+ # collector if there are no other reference except in the map.
179
+ def initialize
180
+ @values = {}
181
+ @references_to_keys_map = {}
182
+ @lock = Monitor.new
183
+ @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
184
+ end
185
+
186
+ # Get a value from the map by key. If the value has been reclaimed by the garbage
187
+ # collector, this will return nil.
188
+ def [](key)
189
+ @lock.synchronize do
190
+ rkey = ref_key(key)
191
+ @values[rkey] if rkey
192
+ end
193
+ end
194
+
195
+ # Add a key/value to the map.
196
+ def []=(key, value)
197
+ ObjectSpace.define_finalizer(key, @reference_cleanup)
198
+ @lock.synchronize do
199
+ @references_to_keys_map[key.__id__] = WeakReference.new(key)
200
+ @values[key.__id__] = value
201
+ end
202
+ end
203
+
204
+ # Remove the value associated with the key from the map.
205
+ def delete(key)
206
+ @lock.synchronize do
207
+ rkey = ref_key(key)
208
+ if rkey
209
+ @references_to_keys_map.delete(rkey)
210
+ @values.delete(rkey)
211
+ else
212
+ nil
213
+ end
214
+ end
215
+ end
216
+
217
+ # Get an array of keys that have not yet been garbage collected.
218
+ def keys
219
+ @values.keys.collect{|rkey| @references_to_keys_map[rkey].object}.compact
220
+ end
221
+
222
+ private
223
+
224
+ def ref_key (key)
225
+ ref = @references_to_keys_map[key.__id__]
226
+ if ref && ref.object
227
+ ref.referenced_object_id
228
+ else
229
+ nil
230
+ end
231
+ end
232
+ end
233
+
234
+ private_constant :WeakKeyMap
235
+ end
236
+ end