concurrent-ruby 0.4.1 → 0.5.0.pre.1
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/README.md +31 -33
- data/lib/concurrent.rb +11 -3
- data/lib/concurrent/actor.rb +29 -29
- data/lib/concurrent/agent.rb +98 -16
- data/lib/concurrent/atomic.rb +125 -0
- data/lib/concurrent/channel.rb +36 -1
- data/lib/concurrent/condition.rb +67 -0
- data/lib/concurrent/copy_on_notify_observer_set.rb +80 -0
- data/lib/concurrent/copy_on_write_observer_set.rb +94 -0
- data/lib/concurrent/count_down_latch.rb +60 -0
- data/lib/concurrent/dataflow.rb +85 -0
- data/lib/concurrent/dereferenceable.rb +69 -31
- data/lib/concurrent/event.rb +27 -21
- data/lib/concurrent/future.rb +103 -43
- data/lib/concurrent/ivar.rb +78 -0
- data/lib/concurrent/mvar.rb +154 -0
- data/lib/concurrent/obligation.rb +94 -9
- data/lib/concurrent/postable.rb +11 -9
- data/lib/concurrent/promise.rb +101 -127
- data/lib/concurrent/safe_task_executor.rb +28 -0
- data/lib/concurrent/scheduled_task.rb +60 -54
- data/lib/concurrent/stoppable.rb +2 -2
- data/lib/concurrent/supervisor.rb +36 -29
- data/lib/concurrent/thread_local_var.rb +117 -0
- data/lib/concurrent/timer_task.rb +28 -30
- data/lib/concurrent/utilities.rb +1 -1
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/agent_spec.rb +121 -230
- data/spec/concurrent/atomic_spec.rb +201 -0
- data/spec/concurrent/condition_spec.rb +171 -0
- data/spec/concurrent/copy_on_notify_observer_set_spec.rb +10 -0
- data/spec/concurrent/copy_on_write_observer_set_spec.rb +10 -0
- data/spec/concurrent/count_down_latch_spec.rb +125 -0
- data/spec/concurrent/dataflow_spec.rb +160 -0
- data/spec/concurrent/dereferenceable_shared.rb +145 -0
- data/spec/concurrent/event_spec.rb +44 -9
- data/spec/concurrent/fixed_thread_pool_spec.rb +0 -1
- data/spec/concurrent/future_spec.rb +184 -69
- data/spec/concurrent/ivar_spec.rb +192 -0
- data/spec/concurrent/mvar_spec.rb +380 -0
- data/spec/concurrent/obligation_spec.rb +193 -0
- data/spec/concurrent/observer_set_shared.rb +233 -0
- data/spec/concurrent/postable_shared.rb +3 -7
- data/spec/concurrent/promise_spec.rb +270 -192
- data/spec/concurrent/safe_task_executor_spec.rb +58 -0
- data/spec/concurrent/scheduled_task_spec.rb +142 -38
- data/spec/concurrent/thread_local_var_spec.rb +113 -0
- data/spec/concurrent/thread_pool_shared.rb +2 -3
- data/spec/concurrent/timer_task_spec.rb +31 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/functions.rb +4 -0
- data/spec/support/less_than_or_equal_to_matcher.rb +5 -0
- metadata +50 -30
- data/lib/concurrent/contract.rb +0 -21
- data/lib/concurrent/event_machine_defer_proxy.rb +0 -22
- data/md/actor.md +0 -404
- data/md/agent.md +0 -142
- data/md/channel.md +0 -40
- data/md/dereferenceable.md +0 -49
- data/md/future.md +0 -125
- data/md/obligation.md +0 -32
- data/md/promise.md +0 -217
- data/md/scheduled_task.md +0 -156
- data/md/supervisor.md +0 -246
- data/md/thread_pool.md +0 -225
- data/md/timer_task.md +0 -191
- data/spec/concurrent/contract_spec.rb +0 -34
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +0 -240
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'concurrent/safe_task_executor'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
describe SafeTaskExecutor do
|
7
|
+
|
8
|
+
describe '#execute' do
|
9
|
+
|
10
|
+
context 'happy execution' do
|
11
|
+
|
12
|
+
let(:task) { Proc.new { 42 } }
|
13
|
+
let(:executor) { SafeTaskExecutor.new(task) }
|
14
|
+
|
15
|
+
it 'should return success' do
|
16
|
+
success, value, reason = executor.execute
|
17
|
+
success.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return task value' do
|
21
|
+
success, value, reason = executor.execute
|
22
|
+
value.should eq 42
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return a nil reason' do
|
26
|
+
success, value, reason = executor.execute
|
27
|
+
reason.should be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'failing execution' do
|
33
|
+
|
34
|
+
let(:task) { Proc.new { raise StandardError.new('an error') } }
|
35
|
+
let(:executor) { SafeTaskExecutor.new(task) }
|
36
|
+
|
37
|
+
it 'should return false success' do
|
38
|
+
success, value, reason = executor.execute
|
39
|
+
success.should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should return a nil value' do
|
43
|
+
success, value, reason = executor.execute
|
44
|
+
value.should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should return the reason' do
|
48
|
+
success, value, reason = executor.execute
|
49
|
+
reason.should be_a(StandardError)
|
50
|
+
reason.message.should eq 'an error'
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'timecop'
|
3
|
+
require_relative 'dereferenceable_shared'
|
3
4
|
require_relative 'obligation_shared'
|
4
5
|
|
5
6
|
module Concurrent
|
@@ -14,77 +15,183 @@ module Concurrent
|
|
14
15
|
let!(:rejected_reason) { StandardError.new('mojo jojo') }
|
15
16
|
|
16
17
|
let(:pending_subject) do
|
17
|
-
ScheduledTask.new(1){ fulfilled_value }
|
18
|
+
ScheduledTask.new(1){ fulfilled_value }.execute
|
18
19
|
end
|
19
20
|
|
20
21
|
let(:fulfilled_subject) do
|
21
|
-
ScheduledTask.new(0.1){ fulfilled_value }.tap
|
22
|
+
ScheduledTask.new(0.1){ fulfilled_value }.execute.tap{ sleep(0.2) }
|
22
23
|
end
|
23
24
|
|
24
25
|
let(:rejected_subject) do
|
25
|
-
ScheduledTask.new(0.1){ raise rejected_reason }.tap
|
26
|
+
ScheduledTask.new(0.1){ raise rejected_reason }.execute.tap{ sleep(0.2) }
|
26
27
|
end
|
27
28
|
|
28
29
|
it_should_behave_like :obligation
|
30
|
+
|
31
|
+
# dereferenceable
|
32
|
+
|
33
|
+
def dereferenceable_subject(value, opts = {})
|
34
|
+
ScheduledTask.execute(0.1, opts){ value }.tap{ sleep(0.2) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def dereferenceable_observable(opts = {})
|
38
|
+
ScheduledTask.new(0.1, opts){ 'value' }
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute_dereferenceable(subject)
|
42
|
+
subject.execute
|
43
|
+
sleep(0.2)
|
44
|
+
end
|
45
|
+
|
46
|
+
it_should_behave_like :dereferenceable
|
29
47
|
end
|
30
48
|
|
31
49
|
context '#initialize' do
|
32
50
|
|
33
|
-
it 'accepts a number of seconds (from now) as the
|
51
|
+
it 'accepts a number of seconds (from now) as the schedule time' do
|
34
52
|
Timecop.freeze do
|
35
53
|
now = Time.now
|
36
|
-
task = ScheduledTask.new(60){ nil }
|
54
|
+
task = ScheduledTask.new(60){ nil }.execute
|
37
55
|
task.schedule_time.to_i.should eq now.to_i + 60
|
38
56
|
end
|
39
57
|
end
|
40
58
|
|
41
59
|
it 'accepts a time object as the schedule time' do
|
42
60
|
schedule = Time.now + (60*10)
|
43
|
-
task = ScheduledTask.new(schedule){ nil }
|
61
|
+
task = ScheduledTask.new(schedule){ nil }.execute
|
44
62
|
task.schedule_time.should eq schedule
|
45
63
|
end
|
46
64
|
|
47
65
|
it 'raises an exception when seconds is less than zero' do
|
48
66
|
expect {
|
49
67
|
ScheduledTask.new(-1){ nil }
|
50
|
-
}.to raise_error(
|
68
|
+
}.to raise_error(ScheduledTask::SchedulingError)
|
51
69
|
end
|
52
70
|
|
53
71
|
it 'raises an exception when schedule time is in the past' do
|
54
72
|
expect {
|
55
73
|
ScheduledTask.new(Time.now - 60){ nil }
|
56
|
-
}.to raise_error(
|
74
|
+
}.to raise_error(ScheduledTask::SchedulingError)
|
57
75
|
end
|
58
76
|
|
59
77
|
it 'raises an exception when no block given' do
|
60
78
|
expect {
|
61
79
|
ScheduledTask.new(1)
|
62
|
-
}.to raise_error(
|
80
|
+
}.to raise_error(ScheduledTask::SchedulingError)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'sets the initial state to :unscheduled' do
|
84
|
+
task = ScheduledTask.new(1){ nil }
|
85
|
+
task.should be_unscheduled
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'sets the #schedule_time to nil prior to execution' do
|
89
|
+
task = ScheduledTask.new(1){ nil }
|
90
|
+
task.schedule_time.should be_nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'instance #execute' do
|
95
|
+
|
96
|
+
it 'does nothing unless the state is :unscheduled' do
|
97
|
+
Thread.should_not_receive(:new).with(any_args)
|
98
|
+
task = ScheduledTask.new(1){ nil }
|
99
|
+
task.instance_variable_set(:@state, :pending)
|
100
|
+
task.execute
|
101
|
+
task.instance_variable_set(:@state, :rejected)
|
102
|
+
task.execute
|
103
|
+
task.instance_variable_set(:@state, :fulfilled)
|
104
|
+
task.execute
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'calculates the #schedule_time on execution' do
|
108
|
+
Timecop.freeze do
|
109
|
+
now = Time.now
|
110
|
+
task = ScheduledTask.new(5){ nil }
|
111
|
+
Timecop.travel(10)
|
112
|
+
task.execute
|
113
|
+
task.schedule_time.to_i.should eq now.to_i + 15
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'raises an exception if expected schedule time is in the past' do
|
118
|
+
Timecop.freeze do
|
119
|
+
schedule = Time.now + (10)
|
120
|
+
task = ScheduledTask.new(schedule){ nil }
|
121
|
+
Timecop.travel(60)
|
122
|
+
expect {
|
123
|
+
task.execute
|
124
|
+
}.to raise_error(ScheduledTask::SchedulingError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'spawns a new thread when a block was given on construction' do
|
129
|
+
Thread.should_receive(:new).with(any_args)
|
130
|
+
task = ScheduledTask.new(1){ nil }
|
131
|
+
task.execute
|
63
132
|
end
|
64
133
|
|
65
|
-
it 'sets the
|
134
|
+
it 'sets the sate to :pending' do
|
66
135
|
task = ScheduledTask.new(1){ nil }
|
136
|
+
task.execute
|
67
137
|
task.should be_pending
|
68
138
|
end
|
139
|
+
|
140
|
+
it 'returns self' do
|
141
|
+
task = ScheduledTask.new(1){ nil }
|
142
|
+
task.execute.should eq task
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'class #execute' do
|
147
|
+
|
148
|
+
it 'creates a new ScheduledTask' do
|
149
|
+
task = ScheduledTask.execute(1){ nil }
|
150
|
+
task.should be_a(ScheduledTask)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'passes the block to the new ScheduledTask' do
|
154
|
+
@expected = false
|
155
|
+
task = ScheduledTask.execute(0.1){ @expected = true }
|
156
|
+
sleep(0.2)
|
157
|
+
@expected.should be_true
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'calls #execute on the new ScheduledTask' do
|
161
|
+
task = ScheduledTask.new(0.1){ nil }
|
162
|
+
ScheduledTask.stub(:new).with(any_args).and_return(task)
|
163
|
+
task.should_receive(:execute).with(no_args)
|
164
|
+
ScheduledTask.execute(0.1){ nil }
|
165
|
+
end
|
69
166
|
end
|
70
167
|
|
71
168
|
context '#cancel' do
|
72
169
|
|
73
170
|
it 'returns false if the task has already been performed' do
|
74
|
-
task = ScheduledTask.new(0.1){ 42 }
|
171
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
75
172
|
sleep(0.2)
|
76
173
|
task.cancel.should be_false
|
77
174
|
end
|
78
175
|
|
79
176
|
it 'returns false if the task is already in progress' do
|
80
|
-
task = ScheduledTask.new(0.1){ sleep(1); 42 }
|
177
|
+
task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
|
81
178
|
sleep(0.2)
|
82
179
|
task.cancel.should be_false
|
83
180
|
end
|
84
181
|
|
182
|
+
it 'cancels the task if it has not yet scheduled' do
|
183
|
+
@expected = true
|
184
|
+
task = ScheduledTask.new(0.1){ @expected = false }
|
185
|
+
task.cancel
|
186
|
+
task.execute
|
187
|
+
sleep(0.5)
|
188
|
+
@expected.should be_true
|
189
|
+
end
|
190
|
+
|
191
|
+
|
85
192
|
it 'cancels the task if it has not yet started' do
|
86
193
|
@expected = true
|
87
|
-
task = ScheduledTask.new(0.3){ @expected = false }
|
194
|
+
task = ScheduledTask.new(0.3){ @expected = false }.execute
|
88
195
|
sleep(0.1)
|
89
196
|
task.cancel
|
90
197
|
sleep(0.5)
|
@@ -92,13 +199,13 @@ module Concurrent
|
|
92
199
|
end
|
93
200
|
|
94
201
|
it 'returns true on success' do
|
95
|
-
task = ScheduledTask.new(0.3){ @expected = false }
|
202
|
+
task = ScheduledTask.new(0.3){ @expected = false }.execute
|
96
203
|
sleep(0.1)
|
97
204
|
task.cancel.should be_true
|
98
205
|
end
|
99
206
|
|
100
207
|
it 'sets the state to :cancelled when cancelled' do
|
101
|
-
task = ScheduledTask.new(10){ 42 }
|
208
|
+
task = ScheduledTask.new(10){ 42 }.execute
|
102
209
|
sleep(0.1)
|
103
210
|
task.cancel
|
104
211
|
task.should be_cancelled
|
@@ -108,7 +215,7 @@ module Concurrent
|
|
108
215
|
context 'execution' do
|
109
216
|
|
110
217
|
it 'sets the state to :in_progress when the task is running' do
|
111
|
-
task = ScheduledTask.new(0.1){ sleep(1); 42 }
|
218
|
+
task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
|
112
219
|
sleep(0.2)
|
113
220
|
task.should be_in_progress
|
114
221
|
end
|
@@ -131,44 +238,42 @@ module Concurrent
|
|
131
238
|
|
132
239
|
let(:observer) { clazz.new }
|
133
240
|
|
134
|
-
it 'returns true for an observer added while :
|
135
|
-
task = ScheduledTask.new(1){ 42 }
|
241
|
+
it 'returns true for an observer added while :unscheduled' do
|
242
|
+
task = ScheduledTask.new(0.1){ 42 }
|
136
243
|
task.add_observer(observer).should be_true
|
137
244
|
end
|
138
245
|
|
139
|
-
it 'returns true for an observer added while :
|
140
|
-
task = ScheduledTask.new(0.1){
|
141
|
-
sleep(0.2)
|
246
|
+
it 'returns true for an observer added while :pending' do
|
247
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
142
248
|
task.add_observer(observer).should be_true
|
143
249
|
end
|
144
250
|
|
145
|
-
it 'returns true for an observer added while
|
146
|
-
task = ScheduledTask.new(1){ 42 }
|
251
|
+
it 'returns true for an observer added while :in_progress' do
|
252
|
+
task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
|
253
|
+
sleep(0.2)
|
147
254
|
task.add_observer(observer).should be_true
|
148
255
|
end
|
149
256
|
|
150
257
|
it 'returns false for an observer added once :cancelled' do
|
151
258
|
task = ScheduledTask.new(1){ 42 }
|
152
|
-
sleep(0.1)
|
153
259
|
task.cancel
|
154
|
-
sleep(0.1)
|
155
260
|
task.add_observer(observer).should be_false
|
156
261
|
end
|
157
262
|
|
158
263
|
it 'returns false for an observer added once :fulfilled' do
|
159
|
-
task = ScheduledTask.new(0.1){ 42 }
|
264
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
160
265
|
sleep(0.2)
|
161
266
|
task.add_observer(observer).should be_false
|
162
267
|
end
|
163
268
|
|
164
269
|
it 'returns false for an observer added once :rejected' do
|
165
|
-
task = ScheduledTask.new(0.1){ raise StandardError }
|
270
|
+
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
166
271
|
sleep(0.2)
|
167
272
|
task.add_observer(observer).should be_false
|
168
273
|
end
|
169
274
|
|
170
275
|
it 'notifies all observers on fulfillment' do
|
171
|
-
task = ScheduledTask.new(0.1){ 42 }
|
276
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
172
277
|
task.add_observer(observer)
|
173
278
|
sleep(0.2)
|
174
279
|
task.value.should == 42
|
@@ -178,7 +283,7 @@ module Concurrent
|
|
178
283
|
end
|
179
284
|
|
180
285
|
it 'notifies all observers on rejection' do
|
181
|
-
task = ScheduledTask.new(0.1){ raise StandardError }
|
286
|
+
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
182
287
|
task.add_observer(observer)
|
183
288
|
sleep(0.2)
|
184
289
|
task.value.should be_nil
|
@@ -188,30 +293,29 @@ module Concurrent
|
|
188
293
|
end
|
189
294
|
|
190
295
|
it 'does not notify an observer added after fulfillment' do
|
191
|
-
observer.should_not_receive(:update).with(any_args
|
192
|
-
task = ScheduledTask.new(0.1){ 42 }
|
296
|
+
observer.should_not_receive(:update).with(any_args)
|
297
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
193
298
|
sleep(0.2)
|
194
299
|
task.add_observer(observer)
|
195
300
|
sleep(0.1)
|
196
301
|
end
|
197
302
|
|
198
303
|
it 'does not notify an observer added after rejection' do
|
199
|
-
observer.should_not_receive(:update).with(any_args
|
200
|
-
task = ScheduledTask.new(0.1){ raise StandardError }
|
304
|
+
observer.should_not_receive(:update).with(any_args)
|
305
|
+
task = ScheduledTask.new(0.1){ raise StandardError }.execute
|
201
306
|
sleep(0.2)
|
202
307
|
task.add_observer(observer)
|
203
308
|
sleep(0.1)
|
204
309
|
end
|
205
310
|
|
206
311
|
it 'does not notify an observer added after cancellation' do
|
207
|
-
observer.should_not_receive(:update).with(any_args
|
208
|
-
task = ScheduledTask.new(0.
|
209
|
-
sleep(0.1)
|
312
|
+
observer.should_not_receive(:update).with(any_args)
|
313
|
+
task = ScheduledTask.new(0.1){ 42 }.execute
|
210
314
|
task.cancel
|
211
|
-
sleep(0.1)
|
212
315
|
task.add_observer(observer)
|
213
|
-
sleep(0.
|
316
|
+
sleep(0.2)
|
214
317
|
end
|
318
|
+
|
215
319
|
end
|
216
320
|
end
|
217
321
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
describe ThreadLocalVar do
|
7
|
+
|
8
|
+
subject{ ThreadLocalVar.new }
|
9
|
+
|
10
|
+
context '#initialize' do
|
11
|
+
|
12
|
+
it 'can set an initial value' do
|
13
|
+
v = ThreadLocalVar.new(14)
|
14
|
+
v.value.should eq 14
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets nil as a default initial value' do
|
18
|
+
v = ThreadLocalVar.new
|
19
|
+
v.value.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets the same initial value for all threads' do
|
23
|
+
v = ThreadLocalVar.new(14)
|
24
|
+
t1 = Thread.new { v.value }
|
25
|
+
t2 = Thread.new { v.value }
|
26
|
+
t1.value.should eq 14
|
27
|
+
t2.value.should eq 14
|
28
|
+
end
|
29
|
+
|
30
|
+
if jruby?
|
31
|
+
it 'uses ThreadLocalJavaStorage' do
|
32
|
+
subject.class.ancestors.should include(Concurrent::ThreadLocalJavaStorage)
|
33
|
+
end
|
34
|
+
elsif rbx? || RbConfig::CONFIG['ruby_version'] =~ /^1\.9/
|
35
|
+
it 'uses ThreadLocalOldStorage' do
|
36
|
+
subject.class.ancestors.should include(Concurrent::ThreadLocalOldStorage)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
it 'uses ThreadLocalNewStorage' do
|
40
|
+
subject.class.ancestors.should include(Concurrent::ThreadLocalNewStorage)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#value' do
|
46
|
+
|
47
|
+
it 'returns the current value' do
|
48
|
+
v = ThreadLocalVar.new(14)
|
49
|
+
v.value.should eq 14
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns the value after modification' do
|
53
|
+
v = ThreadLocalVar.new(14)
|
54
|
+
v.value = 2
|
55
|
+
v.value.should eq 2
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context '#value=' do
|
61
|
+
|
62
|
+
it 'sets a new value' do
|
63
|
+
v = ThreadLocalVar.new(14)
|
64
|
+
v.value = 2
|
65
|
+
v.value.should eq 2
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns the new value' do
|
69
|
+
v = ThreadLocalVar.new(14)
|
70
|
+
(v.value = 2).should eq 2
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does not modify the initial value for other threads' do
|
74
|
+
v = ThreadLocalVar.new(14)
|
75
|
+
v.value = 2
|
76
|
+
t = Thread.new { v.value }
|
77
|
+
t.value.should eq 14
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'does not modify the value for other threads' do
|
81
|
+
v = ThreadLocalVar.new(14)
|
82
|
+
v.value = 2
|
83
|
+
|
84
|
+
b1 = CountDownLatch.new(2)
|
85
|
+
b2 = CountDownLatch.new(2)
|
86
|
+
|
87
|
+
t1 = Thread.new do
|
88
|
+
b1.count_down
|
89
|
+
b1.wait
|
90
|
+
v.value = 1
|
91
|
+
b2.count_down
|
92
|
+
b2.wait
|
93
|
+
v.value
|
94
|
+
end
|
95
|
+
|
96
|
+
t2 = Thread.new do
|
97
|
+
b1.count_down
|
98
|
+
b1.wait
|
99
|
+
v.value = 2
|
100
|
+
b2.count_down
|
101
|
+
b2.wait
|
102
|
+
v.value
|
103
|
+
end
|
104
|
+
|
105
|
+
t1.value.should eq 1
|
106
|
+
t2.value.should eq 2
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|