@@ -33,7 +33,65 @@ module Concurrent
33
33
#
34
34
# @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
35
35
#
36
- class ThreadLocalVar
36
+ # @!visibility private
37
+ class AbstractThreadLocalVar
38
+
39
+ # @!visibility private
40
+ NIL_SENTINEL = Object . new
41
+ private_constant :NIL_SENTINEL
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
48
+ def initialize ( default = nil )
49
+ @default = default
50
+ allocate_storage
51
+ end
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
58
+ def value
59
+ raise NotImplementedError
60
+ end
61
+
62
+ # @!macro [attach] thread_local_var_method_set
63
+ #
64
+ # Sets the current thread's copy of this thread-local variable to the specified value.
65
+ #
66
+ # @param [Object] value the value to set
67
+ # @return [Object] the new value
68
+ def value = ( value )
69
+ raise NotImplementedError
70
+ end
71
+
72
+ # @!macro [attach] thread_local_var_method_bind
73
+ #
74
+ # Bind the given value to thread local storage during
75
+ # execution of the given block.
76
+ #
77
+ # @param [Object] value the value to bind
78
+ # @yield the operation to be performed with the bound variable
79
+ # @return [Object] the value
80
+ def bind ( value , &block )
81
+ raise NotImplementedError
82
+ end
83
+
84
+ protected
85
+
86
+ # @!visibility private
87
+ def allocate_storage
88
+ raise NotImplementedError
89
+ end
90
+ end
91
+
92
+ # @!visibility private
93
+ # @!macro internal_implementation_note
94
+ class RubyThreadLocalVar < AbstractThreadLocalVar
37
95
38
96
# Each thread has a (lazily initialized) array of thread-local variable values
39
97
# Each time a new thread-local var is created, we allocate an "index" for it
@@ -57,31 +115,23 @@ class ThreadLocalVar
57
115
# array, so we don't leak memory
58
116
59
117
# @!visibility private
60
- NIL_SENTINEL = Object . new
61
118
FREE = [ ]
62
119
LOCK = Mutex . new
63
120
ARRAYS = { } # used as a hash set
64
121
@@next = 0
65
- private_constant :NIL_SENTINEL , : FREE, :LOCK , :ARRAYS
122
+ private_constant :FREE , :LOCK , :ARRAYS
66
123
124
+ # @!macro [attach] thread_local_var_method_initialize
125
+ #
67
126
# Creates a thread local variable.
68
127
#
69
128
# @param [Object] default the default value when otherwise unset
70
129
def initialize ( default = nil )
71
130
@default = default
72
- @index = LOCK . synchronize do
73
- FREE . pop || begin
74
- result = @@next
75
- @@next += 1
76
- result
77
- end
78
- end
79
- ObjectSpace . define_finalizer ( self , self . class . threadlocal_finalizer ( @index ) )
131
+ allocate_storage
80
132
end
81
133
82
- # Returns the value in the current thread's copy of this thread-local variable.
83
- #
84
- # @return [Object] the current value
134
+ # @!macro thread_local_var_method_get
85
135
def value
86
136
if array = Thread . current . thread_variable_get ( :__threadlocal_array__ )
87
137
value = array [ @index ]
@@ -97,10 +147,7 @@ def value
97
147
end
98
148
end
99
149
100
- # Sets the current thread's copy of this thread-local variable to the specified value.
101
- #
102
- # @param [Object] value the value to set
103
- # @return [Object] the new value
150
+ # @!macro thread_local_var_method_set
104
151
def value = ( value )
105
152
me = Thread . current
106
153
# We could keep the thread-local arrays in a hash, keyed by Thread
@@ -115,12 +162,7 @@ def value=(value)
115
162
value
116
163
end
117
164
118
- # Bind the given value to thread local storage during
119
- # execution of the given block.
120
- #
121
- # @param [Object] value the value to bind
122
- # @yield the operation to be performed with the bound variable
123
- # @return [Object] the value
165
+ # @!macro thread_local_var_method_bind
124
166
def bind ( value , &block )
125
167
if block_given?
126
168
old_value = self . value
@@ -135,6 +177,18 @@ def bind(value, &block)
135
177
136
178
protected
137
179
180
+ # @!visibility private
181
+ def allocate_storage
182
+ @index = LOCK . synchronize do
183
+ FREE . pop || begin
184
+ result = @@next
185
+ @@next += 1
186
+ result
187
+ end
188
+ end
189
+ ObjectSpace . define_finalizer ( self , self . class . threadlocal_finalizer ( @index ) )
190
+ end
191
+
138
192
# @!visibility private
139
193
def self . threadlocal_finalizer ( index )
140
194
proc do
@@ -160,43 +214,78 @@ def self.thread_finalizer(array)
160
214
end
161
215
end
162
216
end
217
+ end
163
218
164
- private
219
+ if Concurrent . on_jruby?
165
220
166
- # This exists only for use in testing
167
221
# @!visibility private
168
- def value_for ( thread )
169
- if array = thread [ :__threadlocal_array__ ]
170
- value = array [ @index ]
222
+ # @!macro internal_implementation_note
223
+ class JavaThreadLocalVar < AbstractThreadLocalVar
224
+
225
+ # @!macro thread_local_var_method_get
226
+ def value
227
+ value = @var . get
228
+
171
229
if value . nil?
172
230
@default
173
- elsif value . equal? ( NIL_SENTINEL )
231
+ elsif value == NIL_SENTINEL
174
232
nil
175
233
else
176
234
value
177
235
end
178
- else
179
- @default
180
236
end
181
- end
182
237
183
- private
238
+ # @!macro thread_local_var_method_set
239
+ def value = ( value )
240
+ @var . set ( value )
241
+ end
184
242
185
- # This exists only for use in testing
186
- # @!visibility private
187
- def value_for ( thread )
188
- if array = thread [ :__threadlocal_array__ ]
189
- value = array [ @index ]
190
- if value . nil?
191
- @default
192
- elsif value . equal? ( NIL_SENTINEL )
193
- nil
194
- else
195
- value
243
+ # @!macro thread_local_var_method_bind
244
+ def bind ( value , &block )
245
+ if block_given?
246
+ old_value = @var . get
247
+ begin
248
+ @var . set ( value )
249
+ yield
250
+ ensure
251
+ @var . set ( old_value )
252
+ end
196
253
end
197
- else
198
- @default
254
+ end
255
+
256
+ protected
257
+
258
+ # @!visibility private
259
+ def allocate_storage
260
+ @var = java . lang . ThreadLocal . new
199
261
end
200
262
end
201
263
end
264
+
265
+ # @!visibility private
266
+ # @!macro internal_implementation_note
267
+ ThreadLocalVarImplementation = case
268
+ when Concurrent . on_jruby?
269
+ JavaThreadLocalVar
270
+ else
271
+ RubyThreadLocalVar
272
+ end
273
+ private_constant :ThreadLocalVarImplementation
274
+
275
+ # @!macro thread_local_var
276
+ class ThreadLocalVar < ThreadLocalVarImplementation
277
+
278
+ # @!method initialize(default = nil)
279
+ # @!macro thread_local_var_method_initialize
280
+
281
+ # @!method value
282
+ # @!macro thread_local_var_method_get
283
+
284
+ # @!method value=(value)
285
+ # @!macro thread_local_var_method_set
286
+
287
+ # @!method bind(value, &block)
288
+ # @!macro thread_local_var_method_bind
289
+
290
+ end
202
291
end
0 commit comments