Skip to content

Commit ff35d27

Browse files
committed
Merge pull request #390 from ruby-concurrency/rbx-tlv
MRI 1.9.3 & Rbx updates to TLV
2 parents f0d39c5 + 8346f45 commit ff35d27

File tree

4 files changed

+105
-87
lines changed

4 files changed

+105
-87
lines changed

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ install:
99
build: off
1010

1111
test_script:
12-
- RUBYOPT=-w bundle exec rake ci
12+
- bundle exec rake ci
1313

1414
environment:
1515
matrix:

lib/concurrent/atomic/thread_local_var.rb

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def initialize(default = nil)
133133

134134
# @!macro thread_local_var_method_get
135135
def value
136-
if array = Thread.current.thread_variable_get(:__threadlocal_array__)
136+
if array = get_threadlocal_array
137137
value = array[@index]
138138
if value.nil?
139139
@default
@@ -153,8 +153,8 @@ def value=(value)
153153
# We could keep the thread-local arrays in a hash, keyed by Thread
154154
# But why? That would require locking
155155
# Using Ruby's built-in thread-local storage is faster
156-
unless array = me.thread_variable_get(:__threadlocal_array__)
157-
array = me.thread_variable_set(:__threadlocal_array__, [])
156+
unless array = get_threadlocal_array(me)
157+
array = set_threadlocal_array([], me)
158158
LOCK.synchronize { ARRAYS[array.object_id] = array }
159159
ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array))
160160
end
@@ -217,10 +217,31 @@ def self.thread_finalizer(array)
217217

218218
private
219219

220+
if Thread.instance_methods.include?(:thread_variable_get)
221+
222+
def get_threadlocal_array(thread = Thread.current)
223+
thread.thread_variable_get(:__threadlocal_array__)
224+
end
225+
226+
def set_threadlocal_array(array, thread = Thread.current)
227+
thread.thread_variable_set(:__threadlocal_array__, array)
228+
end
229+
230+
else
231+
232+
def get_threadlocal_array(thread = Thread.current)
233+
thread[:__threadlocal_array__]
234+
end
235+
236+
def set_threadlocal_array(array, thread = Thread.current)
237+
thread[:__threadlocal_array__] = array
238+
end
239+
end
240+
220241
# This exists only for use in testing
221242
# @!visibility private
222243
def value_for(thread)
223-
if array = thread.thread_variable_get(:__threadlocal_array__)
244+
if array = get_threadlocal_array(thread)
224245
value = array[@index]
225246
if value.nil?
226247
@default

spec/concurrent/atomic/reentrant_read_write_lock_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
unless Concurrent.on_jruby? || Concurrent.ruby_version(:<, 2)
1+
unless Concurrent.on_jruby?
22

33
# NOTE: These tests depend heavily on the private/undocumented
44
# `ThreadLocalVar#value_for` method. This method does not, and cannot work

spec/concurrent/atomic/thread_local_var_spec.rb

Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,105 @@
1-
unless Concurrent.ruby_version(:<, 2)
1+
require 'rbconfig'
22

3-
require 'rbconfig'
3+
module Concurrent
44

5-
module Concurrent
5+
require 'concurrent/atomic/thread_local_var'
66

7-
require 'concurrent/atomic/thread_local_var'
7+
describe ThreadLocalVar do
88

9-
describe ThreadLocalVar do
9+
subject { ThreadLocalVar.new }
1010

11-
subject { ThreadLocalVar.new }
11+
context '#initialize' do
1212

13-
context '#initialize' do
13+
it 'can set an initial value' do
14+
v = ThreadLocalVar.new(14)
15+
expect(v.value).to eq 14
16+
end
1417

15-
it 'can set an initial value' do
16-
v = ThreadLocalVar.new(14)
17-
expect(v.value).to eq 14
18-
end
18+
it 'sets nil as a default initial value' do
19+
v = ThreadLocalVar.new
20+
expect(v.value).to be_nil
21+
end
1922

20-
it 'sets nil as a default initial value' do
21-
v = ThreadLocalVar.new
22-
expect(v.value).to be_nil
23-
end
23+
it 'sets the same initial value for all threads' do
24+
v = ThreadLocalVar.new(14)
25+
t1 = Thread.new { v.value }
26+
t2 = Thread.new { v.value }
27+
expect(t1.value).to eq 14
28+
expect(t2.value).to eq 14
29+
end
2430

25-
it 'sets the same initial value for all threads' do
26-
v = ThreadLocalVar.new(14)
27-
t1 = Thread.new { v.value }
28-
t2 = Thread.new { v.value }
29-
expect(t1.value).to eq 14
30-
expect(t2.value).to eq 14
31+
if Concurrent.on_jruby?
32+
it 'extends JavaThreadLocalVar' do
33+
expect(subject.class.ancestors).to include(Concurrent::JavaThreadLocalVar)
3134
end
32-
33-
if Concurrent.on_jruby?
34-
it 'extends JavaThreadLocalVar' do
35-
expect(subject.class.ancestors).to include(Concurrent::JavaThreadLocalVar)
36-
end
37-
else
38-
it 'extends RubyThreadLocalVar' do
39-
expect(subject.class.ancestors).to include(Concurrent::RubyThreadLocalVar)
40-
end
35+
else
36+
it 'extends RubyThreadLocalVar' do
37+
expect(subject.class.ancestors).to include(Concurrent::RubyThreadLocalVar)
4138
end
4239
end
40+
end
4341

44-
context '#value' do
42+
context '#value' do
4543

46-
it 'returns the current value' do
47-
v = ThreadLocalVar.new(14)
48-
expect(v.value).to eq 14
49-
end
44+
it 'returns the current value' do
45+
v = ThreadLocalVar.new(14)
46+
expect(v.value).to eq 14
47+
end
5048

51-
it 'returns the value after modification' do
52-
v = ThreadLocalVar.new(14)
53-
v.value = 2
54-
expect(v.value).to eq 2
55-
end
49+
it 'returns the value after modification' do
50+
v = ThreadLocalVar.new(14)
51+
v.value = 2
52+
expect(v.value).to eq 2
53+
end
54+
end
55+
56+
context '#value=' do
57+
58+
it 'sets a new value' do
59+
v = ThreadLocalVar.new(14)
60+
v.value = 2
61+
expect(v.value).to eq 2
5662
end
5763

58-
context '#value=' do
64+
it 'returns the new value' do
65+
v = ThreadLocalVar.new(14)
66+
expect(v.value = 2).to eq 2
67+
end
5968

60-
it 'sets a new value' do
61-
v = ThreadLocalVar.new(14)
62-
v.value = 2
63-
expect(v.value).to eq 2
64-
end
69+
it 'does not modify the initial value for other threads' do
70+
v = ThreadLocalVar.new(14)
71+
v.value = 2
72+
t = Thread.new { v.value }
73+
expect(t.value).to eq 14
74+
end
6575

66-
it 'returns the new value' do
67-
v = ThreadLocalVar.new(14)
68-
expect(v.value = 2).to eq 2
69-
end
76+
it 'does not modify the value for other threads' do
77+
v = ThreadLocalVar.new(14)
78+
v.value = 2
7079

71-
it 'does not modify the initial value for other threads' do
72-
v = ThreadLocalVar.new(14)
73-
v.value = 2
74-
t = Thread.new { v.value }
75-
expect(t.value).to eq 14
80+
b1 = CountDownLatch.new(2)
81+
b2 = CountDownLatch.new(2)
82+
83+
t1 = Thread.new do
84+
b1.count_down
85+
b1.wait
86+
v.value = 1
87+
b2.count_down
88+
b2.wait
89+
v.value
7690
end
7791

78-
it 'does not modify the value for other threads' do
79-
v = ThreadLocalVar.new(14)
92+
t2 = Thread.new do
93+
b1.count_down
94+
b1.wait
8095
v.value = 2
81-
82-
b1 = CountDownLatch.new(2)
83-
b2 = CountDownLatch.new(2)
84-
85-
t1 = Thread.new do
86-
b1.count_down
87-
b1.wait
88-
v.value = 1
89-
b2.count_down
90-
b2.wait
91-
v.value
92-
end
93-
94-
t2 = Thread.new do
95-
b1.count_down
96-
b1.wait
97-
v.value = 2
98-
b2.count_down
99-
b2.wait
100-
v.value
101-
end
102-
103-
expect(t1.value).to eq 1
104-
expect(t2.value).to eq 2
96+
b2.count_down
97+
b2.wait
98+
v.value
10599
end
100+
101+
expect(t1.value).to eq 1
102+
expect(t2.value).to eq 2
106103
end
107104
end
108105
end

0 commit comments

Comments
 (0)