Skip to content

Commit 8dd7e17

Browse files
committed
ThreadLocalVar initial implementation.
1 parent 38e7182 commit 8dd7e17

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

lib/concurrent.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
require 'concurrent/scheduled_task'
2424
require 'concurrent/stoppable'
2525
require 'concurrent/supervisor'
26+
require 'concurrent/threadlocalvar'
2627
require 'concurrent/timer_task'
2728
require 'concurrent/utilities'
2829

lib/concurrent/threadlocalvar.rb

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
module Concurrent
2+
3+
module ThreadLocalSymbolAllocator
4+
5+
protected
6+
7+
def allocate_symbol
8+
# Warning: this will space leak if you create ThreadLocalVars in a loop - not sure what to do about it
9+
@symbol = "thread_local_symbol_#{self.object_id}_#{Time.now.hash}".to_sym
10+
end
11+
12+
end
13+
14+
module ThreadLocalOldStorage
15+
16+
include ThreadLocalSymbolAllocator
17+
18+
protected
19+
20+
def allocate_storage
21+
allocate_symbol
22+
end
23+
24+
def get
25+
Thread.current[@symbol]
26+
end
27+
28+
def set(value)
29+
Thread.current[@symbol] = value
30+
end
31+
32+
end
33+
34+
module ThreadLocalNewStorage
35+
36+
include ThreadLocalSymbolAllocator
37+
38+
protected
39+
40+
def allocate_storage
41+
allocate_symbol
42+
end
43+
44+
def get
45+
Thread.current.thread_variable_get(@symbol)
46+
end
47+
48+
def set(value)
49+
Thread.current.thread_variable_set(@symbol, value)
50+
end
51+
52+
end
53+
54+
module ThreadLocalJavaStorage
55+
56+
protected
57+
58+
def allocate_storage
59+
@var = java.lang.ThreadLocal.new
60+
end
61+
62+
def get
63+
@var.get
64+
end
65+
66+
def set(value)
67+
@var.set(value)
68+
end
69+
70+
end
71+
72+
class ThreadLocalVar
73+
74+
NIL_SENTINEL = Object.new
75+
76+
if defined? java.lang.ThreadLocal.new
77+
include ThreadLocalJavaStorage
78+
elsif Thread.current.respond_to?(:thread_variable_set)
79+
include ThreadLocalNewStorage
80+
else
81+
include ThreadLocalOldStorage
82+
end
83+
84+
def initialize(default = nil)
85+
@default = default
86+
allocate_storage
87+
end
88+
89+
def value
90+
value = get
91+
92+
if value.nil?
93+
@default
94+
elsif value == NIL_SENTINEL
95+
nil
96+
else
97+
value
98+
end
99+
end
100+
101+
def value=(value)
102+
if value.nil?
103+
stored_value = NIL_SENTINEL
104+
else
105+
stored_value = value
106+
end
107+
108+
set stored_value
109+
110+
value
111+
end
112+
113+
end
114+
115+
end
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
require 'spec_helper'
2+
3+
module Concurrent
4+
5+
describe Future do
6+
7+
context '#initialize' do
8+
9+
it 'can set an initial value' do
10+
v = ThreadLocalVar.new(14)
11+
v.value.should eq 14
12+
end
13+
14+
it 'sets nil as a default initial value' do
15+
v = ThreadLocalVar.new
16+
v.value.should be_nil
17+
end
18+
19+
it 'sets the same initial value for all threads' do
20+
v = ThreadLocalVar.new(14)
21+
t1 = Thread.new { v.value }
22+
t2 = Thread.new { v.value }
23+
t1.value.should eq 14
24+
t2.value.should eq 14
25+
end
26+
27+
end
28+
29+
context '#value' do
30+
31+
it 'returns the current value' do
32+
v = ThreadLocalVar.new(14)
33+
v.value.should eq 14
34+
end
35+
36+
it 'returns the value after modification' do
37+
v = ThreadLocalVar.new(14)
38+
v.value = 2
39+
v.value.should eq 2
40+
end
41+
42+
end
43+
44+
context '#value=' do
45+
46+
it 'sets a new value' do
47+
v = ThreadLocalVar.new(14)
48+
v.value = 2
49+
v.value.should eq 2
50+
end
51+
52+
it 'returns the new value' do
53+
v = ThreadLocalVar.new(14)
54+
(v.value = 2).should eq 2
55+
end
56+
57+
it 'does not modify the initial value for other threads' do
58+
v = ThreadLocalVar.new(14)
59+
v.value = 2
60+
t = Thread.new { v.value }
61+
t.value.should eq 14
62+
end
63+
64+
it 'does not modify the value for other threads' do
65+
v = ThreadLocalVar.new(14)
66+
v.value = 2
67+
68+
b1 = CountDownLatch.new(2)
69+
b2 = CountDownLatch.new(2)
70+
71+
t1 = Thread.new do
72+
b1.count_down
73+
b1.wait
74+
v.value = 1
75+
b2.count_down
76+
b2.wait
77+
v.value
78+
end
79+
80+
t2 = Thread.new do
81+
b1.count_down
82+
b1.wait
83+
v.value = 2
84+
b2.count_down
85+
b2.wait
86+
v.value
87+
end
88+
89+
t1.value.should eq 1
90+
t2.value.should eq 2
91+
end
92+
93+
end
94+
95+
end
96+
97+
end

0 commit comments

Comments
 (0)