concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/lib/concurrent.rb +9 -29
  4. data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
  5. data/lib/concurrent/actor/actor_context.rb +77 -0
  6. data/lib/concurrent/actor/actor_ref.rb +67 -0
  7. data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
  8. data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
  9. data/lib/concurrent/actors.rb +5 -0
  10. data/lib/concurrent/agent.rb +81 -47
  11. data/lib/concurrent/async.rb +35 -35
  12. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  13. data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
  14. data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
  15. data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
  16. data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
  17. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  18. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  19. data/lib/concurrent/atomic/event.rb +103 -0
  20. data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
  21. data/lib/concurrent/atomics.rb +9 -0
  22. data/lib/concurrent/channel/buffered_channel.rb +6 -4
  23. data/lib/concurrent/channel/channel.rb +30 -2
  24. data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
  25. data/lib/concurrent/channel/waitable_list.rb +3 -1
  26. data/lib/concurrent/channels.rb +5 -0
  27. data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
  28. data/lib/concurrent/collection/priority_queue.rb +305 -0
  29. data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
  30. data/lib/concurrent/collections.rb +3 -0
  31. data/lib/concurrent/configuration.rb +68 -19
  32. data/lib/concurrent/dataflow.rb +9 -9
  33. data/lib/concurrent/delay.rb +21 -13
  34. data/lib/concurrent/dereferenceable.rb +40 -33
  35. data/lib/concurrent/exchanger.rb +3 -0
  36. data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
  37. data/lib/concurrent/executor/executor.rb +222 -0
  38. data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
  39. data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
  40. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  41. data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
  42. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  43. data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
  44. data/lib/concurrent/executor/one_by_one.rb +65 -0
  45. data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
  46. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  47. data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
  48. data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
  49. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
  50. data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
  51. data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
  52. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  53. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  54. data/lib/concurrent/executor/timer_set.rb +138 -0
  55. data/lib/concurrent/executors.rb +9 -0
  56. data/lib/concurrent/future.rb +39 -40
  57. data/lib/concurrent/ivar.rb +22 -15
  58. data/lib/concurrent/mvar.rb +2 -1
  59. data/lib/concurrent/obligation.rb +9 -3
  60. data/lib/concurrent/observable.rb +33 -0
  61. data/lib/concurrent/options_parser.rb +46 -0
  62. data/lib/concurrent/promise.rb +23 -24
  63. data/lib/concurrent/scheduled_task.rb +21 -45
  64. data/lib/concurrent/timer_task.rb +204 -126
  65. data/lib/concurrent/tvar.rb +1 -1
  66. data/lib/concurrent/utilities.rb +3 -36
  67. data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
  68. data/lib/concurrent/utility/timeout.rb +36 -0
  69. data/lib/concurrent/utility/timer.rb +21 -0
  70. data/lib/concurrent/version.rb +1 -1
  71. data/lib/concurrent_ruby_ext.bundle +0 -0
  72. data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
  73. data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
  74. data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
  75. data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
  76. data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
  77. data/spec/concurrent/agent_spec.rb +160 -71
  78. data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
  79. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
  80. data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
  81. data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
  82. data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
  83. data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
  84. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
  85. data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
  86. data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
  87. data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
  88. data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
  89. data/spec/concurrent/channel/channel_spec.rb +6 -4
  90. data/spec/concurrent/channel/probe_spec.rb +37 -9
  91. data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
  92. data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
  93. data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
  94. data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
  95. data/spec/concurrent/configuration_spec.rb +4 -70
  96. data/spec/concurrent/dereferenceable_shared.rb +5 -4
  97. data/spec/concurrent/exchanger_spec.rb +10 -5
  98. data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
  99. data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
  100. data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
  101. data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
  102. data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
  103. data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
  104. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
  105. data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
  106. data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
  107. data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
  108. data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
  109. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
  110. data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
  111. data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
  112. data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
  113. data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
  114. data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
  115. data/spec/concurrent/executor/timer_set_spec.rb +183 -0
  116. data/spec/concurrent/future_spec.rb +12 -0
  117. data/spec/concurrent/ivar_spec.rb +11 -1
  118. data/spec/concurrent/observable_shared.rb +173 -0
  119. data/spec/concurrent/observable_spec.rb +51 -0
  120. data/spec/concurrent/options_parser_spec.rb +71 -0
  121. data/spec/concurrent/runnable_shared.rb +6 -0
  122. data/spec/concurrent/scheduled_task_spec.rb +60 -40
  123. data/spec/concurrent/timer_task_spec.rb +130 -144
  124. data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
  125. data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
  126. data/spec/concurrent/utility/timer_spec.rb +52 -0
  127. metadata +147 -108
  128. data/lib/concurrent/actor_context.rb +0 -31
  129. data/lib/concurrent/actor_ref.rb +0 -39
  130. data/lib/concurrent/atomic.rb +0 -121
  131. data/lib/concurrent/channel/probe.rb +0 -19
  132. data/lib/concurrent/count_down_latch.rb +0 -60
  133. data/lib/concurrent/event.rb +0 -80
  134. data/lib/concurrent/java_cached_thread_pool.rb +0 -45
  135. data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
  136. data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
  137. data/lib/concurrent/simple_actor_ref.rb +0 -124
  138. data/lib/concurrent/thread_pool_executor.rb +0 -30
  139. data/spec/concurrent/atomic_spec.rb +0 -201
  140. data/spec/concurrent/count_down_latch_spec.rb +0 -125
  141. data/spec/concurrent/safe_task_executor_spec.rb +0 -58
  142. data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ share_examples_for :atomic_fixnum do
4
+
5
+ context 'construction' do
6
+
7
+ it 'sets the initial value' do
8
+ described_class.new(10).value.should eq 10
9
+ end
10
+
11
+ it 'defaults the initial value to zero' do
12
+ described_class.new.value.should eq 0
13
+ end
14
+
15
+ it 'raises en exception if the initial value is not a Fixnum' do
16
+ lambda {
17
+ described_class.new(10.01)
18
+ }.should raise_error
19
+ end
20
+ end
21
+
22
+ context '#value' do
23
+
24
+ it 'returns the current value' do
25
+ counter = described_class.new(10)
26
+ counter.value.should eq 10
27
+ counter.increment
28
+ counter.value.should eq 11
29
+ counter.decrement
30
+ counter.value.should eq 10
31
+ end
32
+ end
33
+
34
+ context '#value=' do
35
+
36
+ it 'sets the #value to the given `Fixnum`' do
37
+ atomic = described_class.new(0)
38
+ atomic.value = 10
39
+ atomic.value.should eq 10
40
+ end
41
+
42
+ it 'returns the new value' do
43
+ atomic = described_class.new(0)
44
+ (atomic.value = 10).should eq 10
45
+ end
46
+
47
+ it 'raises and exception if the value is not a `Fixnum`' do
48
+ atomic = described_class.new(0)
49
+ expect {
50
+ atomic.value = 'foo'
51
+ }.to raise_error
52
+ end
53
+ end
54
+
55
+ context '#increment' do
56
+
57
+ it 'increases the value by one' do
58
+ counter = described_class.new(10)
59
+ 3.times{ counter.increment }
60
+ counter.value.should eq 13
61
+ end
62
+
63
+ it 'returns the new value' do
64
+ counter = described_class.new(10)
65
+ counter.increment.should eq 11
66
+ end
67
+
68
+ it 'is aliased as #up' do
69
+ described_class.new(10).up.should eq 11
70
+ end
71
+ end
72
+
73
+ context '#decrement' do
74
+
75
+ it 'decreases the value by one' do
76
+ counter = described_class.new(10)
77
+ 3.times{ counter.decrement }
78
+ counter.value.should eq 7
79
+ end
80
+
81
+ it 'returns the new value' do
82
+ counter = described_class.new(10)
83
+ counter.decrement.should eq 9
84
+ end
85
+
86
+ it 'is aliased as #down' do
87
+ described_class.new(10).down.should eq 9
88
+ end
89
+ end
90
+
91
+ context '#compare_and_set' do
92
+
93
+ it 'returns false if the value is not found' do
94
+ described_class.new(14).compare_and_set(2, 14).should eq false
95
+ end
96
+
97
+ it 'returns true if the value is found' do
98
+ described_class.new(14).compare_and_set(14, 2).should eq true
99
+ end
100
+
101
+ it 'sets if the value is found' do
102
+ f = described_class.new(14)
103
+ f.compare_and_set(14, 2)
104
+ f.value.should eq 2
105
+ end
106
+
107
+ it 'does not set if the value is not found' do
108
+ f = described_class.new(14)
109
+ f.compare_and_set(2, 12)
110
+ f.value.should eq 14
111
+ end
112
+ end
113
+ end
114
+
115
+ module Concurrent
116
+
117
+ describe MutexAtomicFixnum do
118
+
119
+ it_should_behave_like :atomic_fixnum
120
+
121
+ specify 'construction is synchronized' do
122
+ mutex = double('mutex')
123
+ Mutex.should_receive(:new).once.with(no_args).and_return(mutex)
124
+ described_class.new
125
+ end
126
+
127
+ specify 'value is synchronized' do
128
+ mutex = double('mutex')
129
+ Mutex.stub(:new).with(no_args).and_return(mutex)
130
+ mutex.should_receive(:lock)
131
+ mutex.should_receive(:unlock)
132
+ described_class.new.value
133
+ end
134
+
135
+ specify 'value= is synchronized' do
136
+ mutex = double('mutex')
137
+ Mutex.stub(:new).with(no_args).and_return(mutex)
138
+ mutex.should_receive(:lock)
139
+ mutex.should_receive(:unlock)
140
+ described_class.new.value = 10
141
+ end
142
+
143
+ specify 'increment is synchronized' do
144
+ mutex = double('mutex')
145
+ Mutex.stub(:new).with(no_args).and_return(mutex)
146
+ mutex.should_receive(:lock)
147
+ mutex.should_receive(:unlock)
148
+ described_class.new.increment
149
+ end
150
+
151
+ specify 'decrement is synchronized' do
152
+ mutex = double('mutex')
153
+ Mutex.stub(:new).with(no_args).and_return(mutex)
154
+ mutex.should_receive(:lock)
155
+ mutex.should_receive(:unlock)
156
+ described_class.new.decrement
157
+ end
158
+
159
+ specify 'compare_and_set is synchronized' do
160
+ mutex = double('mutex')
161
+ Mutex.stub(:new).with(no_args).and_return(mutex)
162
+ mutex.should_receive(:lock)
163
+ mutex.should_receive(:unlock)
164
+ described_class.new(14).compare_and_set(14, 2)
165
+ end
166
+ end
167
+
168
+ if jruby?
169
+
170
+ describe JavaAtomicFixnum do
171
+ it_should_behave_like :atomic_fixnum
172
+ end
173
+ end
174
+
175
+ describe AtomicFixnum do
176
+ if jruby?
177
+ it 'inherits from JavaAtomicFixnum' do
178
+ AtomicFixnum.ancestors.should include(JavaAtomicFixnum)
179
+ end
180
+ else
181
+ it 'inherits from MutexAtomicFixnum' do
182
+ AtomicFixnum.ancestors.should include(MutexAtomicFixnum)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -86,7 +86,7 @@ module Concurrent
86
86
  sleep(0.1)
87
87
  result.should be_woken_up
88
88
  result.should_not be_timed_out
89
- result.remaining_time.should be_within(0.05).of(0.87)
89
+ result.remaining_time.should be_within(0.1).of(0.85)
90
90
  t.status.should be_false
91
91
  end
92
92
 
@@ -98,7 +98,7 @@ module Concurrent
98
98
  sleep(0.1)
99
99
  result.should be_woken_up
100
100
  result.should_not be_timed_out
101
- result.remaining_time.should be_within(0.05).of(0.87)
101
+ result.remaining_time.should be_within(0.1).of(0.85)
102
102
  t.status.should be_false
103
103
  end
104
104
 
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ share_examples_for :count_down_latch do
4
+
5
+ let(:latch) { described_class.new(3) }
6
+ let(:zero_count_latch) { described_class.new(0) }
7
+
8
+ context '#initialize' do
9
+
10
+ it 'raises an exception if the initial count is less than zero' do
11
+ expect {
12
+ described_class.new(-1)
13
+ }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'raises an exception if the initial count is not an integer' do
17
+ expect {
18
+ described_class.new('foo')
19
+ }.to raise_error(ArgumentError)
20
+ end
21
+ end
22
+
23
+ describe '#count' do
24
+
25
+ it 'should be the value passed to the constructor' do
26
+ latch.count.should eq 3
27
+ end
28
+
29
+ it 'should be decreased after every count down' do
30
+ latch.count_down
31
+ latch.count.should eq 2
32
+ end
33
+
34
+ it 'should not go below zero' do
35
+ 5.times { latch.count_down }
36
+ latch.count.should eq 0
37
+ end
38
+ end
39
+
40
+ describe '#wait' do
41
+
42
+ context 'count set to zero' do
43
+ it 'should return true immediately' do
44
+ result = zero_count_latch.wait
45
+ result.should be_true
46
+ end
47
+
48
+ it 'should return true immediately with timeout' do
49
+ result = zero_count_latch.wait(5)
50
+ result.should be_true
51
+ end
52
+ end
53
+
54
+ context 'non zero count' do
55
+
56
+ it 'should block thread until counter is set to zero' do
57
+ 3.times do
58
+ Thread.new { sleep(0.1); latch.count_down }
59
+ end
60
+
61
+ result = latch.wait
62
+ result.should be_true
63
+ latch.count.should eq 0
64
+ end
65
+
66
+ it 'should block until counter is set to zero with timeout' do
67
+ 3.times do
68
+ Thread.new { sleep(0.1); latch.count_down }
69
+ end
70
+
71
+ result = latch.wait(1)
72
+ result.should be_true
73
+ latch.count.should eq 0
74
+
75
+ end
76
+
77
+ it 'should block until timeout and return false when counter is not set to zero' do
78
+ result = latch.wait(0.1)
79
+ result.should be_false
80
+ latch.count.should eq 3
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ module Concurrent
87
+
88
+ describe MutexCountDownLatch do
89
+
90
+ it_should_behave_like :count_down_latch
91
+
92
+ context 'spurious wake ups' do
93
+
94
+ subject { described_class.new(3) }
95
+
96
+ before(:each) do
97
+ def subject.simulate_spurious_wake_up
98
+ @mutex.synchronize do
99
+ @condition.signal
100
+ @condition.broadcast
101
+ end
102
+ end
103
+ end
104
+
105
+ it 'should resist to spurious wake ups without timeout' do
106
+ @expected = false
107
+ Thread.new { subject.wait; @expected = true }
108
+
109
+ sleep(0.1)
110
+ subject.simulate_spurious_wake_up
111
+
112
+ sleep(0.1)
113
+ @expected.should be_false
114
+ end
115
+
116
+ it 'should resist to spurious wake ups with timeout' do
117
+ @expected = false
118
+ Thread.new { subject.wait(0.5); @expected = true }
119
+
120
+ sleep(0.1)
121
+ subject.simulate_spurious_wake_up
122
+
123
+ sleep(0.1)
124
+ @expected.should be_false
125
+
126
+ sleep(0.4)
127
+ @expected.should be_true
128
+ end
129
+ end
130
+ end
131
+
132
+ if jruby?
133
+
134
+ describe JavaCountDownLatch do
135
+
136
+ it_should_behave_like :count_down_latch
137
+ end
138
+ end
139
+
140
+ describe CountDownLatch do
141
+ if jruby?
142
+ it 'inherits from JavaCountDownLatch' do
143
+ CountDownLatch.ancestors.should include(JavaCountDownLatch)
144
+ end
145
+ else
146
+ it 'inherits from MutexCountDownLatch' do
147
+ CountDownLatch.ancestors.should include(MutexCountDownLatch)
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe CyclicBarrier do
6
+
7
+ let(:parties) { 3 }
8
+ let!(:barrier) { described_class.new(3) }
9
+
10
+ context '#initialize' do
11
+
12
+ it 'raises an exception if the initial count is less than 1' do
13
+ expect {
14
+ described_class.new(0)
15
+ }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'raises an exception if the initial count is not an integer' do
19
+ expect {
20
+ described_class.new('foo')
21
+ }.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+
25
+ describe '#parties' do
26
+
27
+ it 'should be the value passed to the constructor' do
28
+ barrier.parties.should eq 3
29
+ end
30
+
31
+ end
32
+
33
+ describe '#number_waiting' do
34
+ context 'without any waiting thread' do
35
+ it 'should be equal to zero' do
36
+ barrier.number_waiting.should eq 0
37
+ end
38
+ end
39
+
40
+ context 'with waiting threads' do
41
+ it 'should be equal to the waiting threads count' do
42
+ Thread.new { barrier.wait }
43
+ Thread.new { barrier.wait }
44
+
45
+ sleep(0.1)
46
+
47
+ barrier.number_waiting.should eq 2
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#broken?' do
53
+ it 'should not be broken when created' do
54
+ barrier.broken?.should eq false
55
+ end
56
+
57
+ it 'should not be broken when reset is called without waiting thread' do
58
+ barrier.reset
59
+ barrier.broken?.should eq false
60
+ end
61
+ end
62
+
63
+ describe 'reset' do
64
+ it 'should release all waiting threads' do
65
+ latch = CountDownLatch.new(1)
66
+
67
+ Thread.new { barrier.wait; latch.count_down }
68
+ sleep(0.1)
69
+ barrier.reset
70
+ latch.wait(0.1).should be_true
71
+
72
+ barrier.should_not be_broken
73
+ barrier.number_waiting.should eq 0
74
+ end
75
+ end
76
+
77
+ describe '#wait' do
78
+ context 'without timeout' do
79
+ it 'should block the thread' do
80
+ t = Thread.new { barrier.wait }
81
+ sleep(0.1)
82
+
83
+ t.status.should eq 'sleep'
84
+ end
85
+
86
+ it 'should release all threads when their number matches the desired one' do
87
+ latch = CountDownLatch.new(parties)
88
+
89
+ parties.times { Thread.new { barrier.wait; latch.count_down } }
90
+ latch.wait(0.1).should be_true
91
+ barrier.number_waiting.should eq 0
92
+ barrier.should_not be_broken
93
+ end
94
+
95
+ it 'returns true when released' do
96
+ latch = CountDownLatch.new(parties)
97
+
98
+ parties.times { Thread.new { latch.count_down if barrier.wait == true } }
99
+ latch.wait(0.1).should be_true
100
+ end
101
+
102
+ it 'executes the block once' do
103
+ counter = AtomicFixnum.new
104
+ barrier = described_class.new(parties) { counter.increment }
105
+
106
+ latch = CountDownLatch.new(parties)
107
+
108
+ parties.times { Thread.new { latch.count_down if barrier.wait == true } }
109
+ latch.wait(0.1).should be_true
110
+
111
+ counter.value.should eq 1
112
+ end
113
+
114
+ it 'can be reused' do
115
+ first_latch = CountDownLatch.new(parties)
116
+ parties.times { Thread.new { barrier.wait; first_latch.count_down } }
117
+ first_latch.wait(0.1).should be_true
118
+
119
+ latch = CountDownLatch.new(parties)
120
+ parties.times { Thread.new { barrier.wait; latch.count_down } }
121
+ latch.wait(0.1).should be_true
122
+ end
123
+
124
+ it 'return false if barrier has been reset' do
125
+ latch = CountDownLatch.new(1)
126
+
127
+ Thread.new { latch.count_down if barrier.wait == false }
128
+ sleep(0.1)
129
+ barrier.reset
130
+ latch.wait(0.1).should be_true
131
+ end
132
+ end
133
+
134
+ context 'with timeout' do
135
+ context 'timeout not expiring' do
136
+ it 'should block the thread' do
137
+ t = Thread.new { barrier.wait(1) }
138
+ sleep(0.1)
139
+
140
+ t.status.should eq 'sleep'
141
+ end
142
+
143
+ it 'should release all threads when their number matches the desired one' do
144
+ latch = CountDownLatch.new(parties)
145
+
146
+ parties.times { Thread.new { barrier.wait(1); latch.count_down } }
147
+ latch.wait(0.2).should be_true
148
+ barrier.number_waiting.should eq 0
149
+ end
150
+
151
+ it 'returns true when released' do
152
+ latch = CountDownLatch.new(parties)
153
+
154
+ parties.times { Thread.new { latch.count_down if barrier.wait(1) == true } }
155
+ latch.wait(0.1).should be_true
156
+ end
157
+ end
158
+
159
+ context 'timeout expiring' do
160
+
161
+ it 'returns false' do
162
+ latch = CountDownLatch.new(1)
163
+
164
+ Thread.new { latch.count_down if barrier.wait(0.1) == false }
165
+ latch.wait(0.2).should be_true
166
+ end
167
+
168
+ it 'breaks the barrier and release all other threads' do
169
+ latch = CountDownLatch.new(2)
170
+
171
+ Thread.new { barrier.wait(0.1); latch.count_down }
172
+ Thread.new { barrier.wait; latch.count_down }
173
+
174
+ latch.wait(0.2).should be_true
175
+ barrier.should be_broken
176
+ end
177
+
178
+ it 'does not execute the block on timeout' do
179
+ counter = AtomicFixnum.new
180
+ barrier = described_class.new(parties) { counter.increment }
181
+
182
+ barrier.wait(0.1)
183
+
184
+ counter.value.should eq 0
185
+ end
186
+ end
187
+ end
188
+
189
+ context '#broken barrier' do
190
+ it 'should not accept new threads' do
191
+ Thread.new { barrier.wait(0.1) }
192
+ sleep(0.2)
193
+
194
+ barrier.should be_broken
195
+
196
+ barrier.wait.should be_false
197
+ end
198
+
199
+ it 'can be reset' do
200
+ Thread.new { barrier.wait(0.1) }
201
+ sleep(0.2)
202
+
203
+ barrier.should be_broken
204
+
205
+ barrier.reset
206
+
207
+ barrier.should_not be_broken
208
+ end
209
+ end
210
+ end
211
+
212
+ context 'spurious wake ups' do
213
+
214
+ before(:each) do
215
+ def barrier.simulate_spurious_wake_up
216
+ @mutex.synchronize do
217
+ @condition.signal
218
+ @condition.broadcast
219
+ end
220
+ end
221
+ end
222
+
223
+ it 'should resist to spurious wake ups without timeout' do
224
+ @expected = false
225
+ Thread.new { barrier.wait; @expected = true }
226
+
227
+ sleep(0.1)
228
+ barrier.simulate_spurious_wake_up
229
+
230
+ sleep(0.1)
231
+ @expected.should be_false
232
+ end
233
+
234
+ it 'should resist to spurious wake ups with timeout' do
235
+ @expected = false
236
+ Thread.new { barrier.wait(0.5); @expected = true }
237
+
238
+ sleep(0.1)
239
+ barrier.simulate_spurious_wake_up
240
+
241
+ sleep(0.1)
242
+ @expected.should be_false
243
+
244
+ end
245
+ end
246
+ end
247
+
248
+ end