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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -2
- data/README.md +103 -54
- data/lib/concurrent.rb +34 -14
- data/lib/concurrent/async.rb +164 -50
- data/lib/concurrent/atom.rb +171 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +27 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +25 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +226 -112
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +10 -0
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +70 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +208 -134
- data/lib/concurrent/scheduled_task.rb +339 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +35 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +24 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +87 -100
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +40 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- metadata +47 -83
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -52
- data/lib/concurrent/utility/processor_count.rb +0 -152
- 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]
|
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
|
-
|
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
|
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
|
-
# @!
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|