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

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.
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