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
@@ -1,34 +1,57 @@
1
1
  require 'spec_helper'
2
- require_relative 'runnable_shared'
3
- require_relative 'stoppable_shared'
2
+ require_relative 'dereferenceable_shared'
3
+ require_relative 'observable_shared'
4
4
 
5
5
  module Concurrent
6
6
 
7
7
  describe TimerTask do
8
8
 
9
- after(:each) do
10
- @subject = @subject.runner if @subject.respond_to?(:runner)
11
- @subject.kill unless @subject.nil?
12
- @thread.kill unless @thread.nil?
13
- sleep(0.1)
9
+ before(:each) do
10
+ # suppress deprecation warnings.
11
+ Concurrent::TimerTask.any_instance.stub(:warn)
12
+ Concurrent::TimerTask.stub(:warn)
14
13
  end
15
14
 
16
- context ':runnable' do
15
+ context :dereferenceable do
17
16
 
18
- subject { TimerTask.new{ nil } }
17
+ after(:each) do
18
+ begin
19
+ @subject.kill if @subject
20
+ rescue Exception => ex
21
+ # prevent exceptions with mocks in tests
22
+ end
23
+ end
24
+
25
+ def dereferenceable_subject(value, opts = {})
26
+ opts = opts.merge(execution_interval: 0.1, run_now: true)
27
+ @subject = TimerTask.new(opts){ value }.execute.tap{ sleep(0.1) }
28
+ end
29
+
30
+ def dereferenceable_observable(opts = {})
31
+ opts = opts.merge(execution_interval: 0.1, run_now: true)
32
+ @subject = TimerTask.new(opts){ 'value' }
33
+ end
19
34
 
20
- it_should_behave_like :runnable
35
+ def execute_dereferenceable(subject)
36
+ subject.execute
37
+ sleep(0.1)
38
+ end
39
+
40
+ it_should_behave_like :dereferenceable
21
41
  end
22
42
 
23
- context ':stoppable' do
43
+ context :observable do
44
+
45
+ subject{ TimerTask.new(execution_interval: 0.1){ nil } }
46
+
47
+ after(:each){ subject.kill }
24
48
 
25
- subject do
26
- task = TimerTask.new{ nil }
27
- task.run!
28
- task
49
+ def trigger_observable(observable)
50
+ observable.execute
51
+ sleep(0.2)
29
52
  end
30
53
 
31
- it_should_behave_like :stoppable
54
+ it_should_behave_like :observable
32
55
  end
33
56
 
34
57
  context 'created with #new' do
@@ -37,188 +60,148 @@ module Concurrent
37
60
 
38
61
  it 'raises an exception if no block given' do
39
62
  lambda {
40
- @subject = Concurrent::TimerTask.new
63
+ Concurrent::TimerTask.new
41
64
  }.should raise_error(ArgumentError)
42
65
  end
43
66
 
44
67
  it 'raises an exception if :execution_interval is not greater than zero' do
45
68
  lambda {
46
- @subject = Concurrent::TimerTask.new(execution_interval: 0){ nil }
69
+ Concurrent::TimerTask.new(execution_interval: 0){ nil }
47
70
  }.should raise_error(ArgumentError)
48
71
  end
49
72
 
50
73
  it 'raises an exception if :execution_interval is not an integer' do
51
74
  lambda {
52
- @subject = Concurrent::TimerTask.new(execution_interval: 'one'){ nil }
75
+ Concurrent::TimerTask.new(execution_interval: 'one'){ nil }
53
76
  }.should raise_error(ArgumentError)
54
77
  end
55
78
 
56
79
  it 'raises an exception if :timeout_interval is not greater than zero' do
57
80
  lambda {
58
- @subject = Concurrent::TimerTask.new(timeout_interval: 0){ nil }
81
+ Concurrent::TimerTask.new(timeout_interval: 0){ nil }
59
82
  }.should raise_error(ArgumentError)
60
83
  end
61
84
 
62
85
  it 'raises an exception if :timeout_interval is not an integer' do
63
86
  lambda {
64
- @subject = Concurrent::TimerTask.new(timeout_interval: 'one'){ nil }
87
+ Concurrent::TimerTask.new(timeout_interval: 'one'){ nil }
65
88
  }.should raise_error(ArgumentError)
66
89
  end
67
90
 
68
91
  it 'uses the default execution interval when no interval is given' do
69
- @subject = TimerTask.new{ nil }
70
- @subject.execution_interval.should eq TimerTask::EXECUTION_INTERVAL
92
+ subject = TimerTask.new{ nil }
93
+ subject.execution_interval.should eq TimerTask::EXECUTION_INTERVAL
71
94
  end
72
95
 
73
96
  it 'uses the default timeout interval when no interval is given' do
74
- @subject = TimerTask.new{ nil }
75
- @subject.timeout_interval.should eq TimerTask::TIMEOUT_INTERVAL
97
+ subject = TimerTask.new{ nil }
98
+ subject.timeout_interval.should eq TimerTask::TIMEOUT_INTERVAL
76
99
  end
77
100
 
78
101
  it 'uses the given execution interval' do
79
- @subject = TimerTask.new(execution_interval: 5){ nil }
80
- @subject.execution_interval.should eq 5
102
+ subject = TimerTask.new(execution_interval: 5){ nil }
103
+ subject.execution_interval.should eq 5
81
104
  end
82
105
 
83
106
  it 'uses the given timeout interval' do
84
- @subject = TimerTask.new(timeout_interval: 5){ nil }
85
- @subject.timeout_interval.should eq 5
107
+ subject = TimerTask.new(timeout_interval: 5){ nil }
108
+ subject.timeout_interval.should eq 5
86
109
  end
87
110
  end
88
111
 
89
112
  context '#kill' do
90
113
 
91
- it 'kills its threads while sleeping' do
92
- Thread.should_receive(:kill).at_least(:once).times.with(any_args)
93
- task = TimerTask.new(run_now: false){ nil }
94
- task.run!
95
- sleep(0.1)
96
- task.kill
97
- end
98
-
99
- it 'kills its threads once executing' do
100
- Thread.should_receive(:kill).at_least(2).times.with(any_args)
101
- task = TimerTask.new(run_now: true){ nil }
102
- task.run!
103
- sleep(0.1)
104
- task.kill
105
- end
106
-
107
114
  it 'returns true on success' do
108
- task = TimerTask.new(run_now: false){ nil }
109
- task.run!
115
+ task = TimerTask.execute(run_now: false){ nil }
110
116
  sleep(0.1)
111
117
  task.kill.should be_true
112
118
  end
113
-
114
- it 'returns false on exception' do
115
- Thread.stub(:kill).with(any_args).and_raise(StandardError)
116
- task = TimerTask.new(run_now: false){ nil }
117
- task.run!
118
- sleep(0.1)
119
- task.kill.should be_false
120
- end
121
119
  end
122
120
  end
123
121
 
124
- context 'created with TimerTask.run!' do
122
+ context 'arguments' do
125
123
 
126
- context 'arguments' do
124
+ it 'raises an exception if no block given' do
125
+ lambda {
126
+ Concurrent::TimerTask.execute
127
+ }.should raise_error
128
+ end
127
129
 
128
- it 'raises an exception if no block given' do
129
- lambda {
130
- @subject = Concurrent::TimerTask.run
131
- }.should raise_error
132
- end
130
+ specify '#execution_interval is writeable' do
133
131
 
134
- it 'passes the block to the new TimerTask' do
135
- @expected = false
136
- block = proc{ @expected = true }
137
- @subject = TimerTask.run!(run_now: true, &block)
138
- sleep(0.1)
139
- @expected.should be_true
132
+ latch = CountDownLatch.new(1)
133
+ subject = TimerTask.new(execution_interval: 1) do |task|
134
+ task.execution_interval = 3
135
+ latch.count_down
140
136
  end
141
137
 
142
- it 'creates a new thread' do
143
- thread = Thread.new{ sleep(1) }
144
- Thread.should_receive(:new).with(any_args()).and_return(thread)
145
- @subject = TimerTask.run!{ nil }
146
- end
138
+ subject.execution_interval.should == 1
139
+ subject.execution_interval = 0.1
140
+ subject.execution_interval.should == 0.1
147
141
 
148
- specify '#execution_interval is writeable' do
149
- @subject = TimerTask.new(execution_interval: 1) do |task|
150
- task.execution_interval = 3
151
- end
152
- @subject.execution_interval.should == 1
153
- @subject.execution_interval = 0.1
154
- @subject.execution_interval.should == 0.1
155
- @thread = Thread.new { @subject.run }
156
- sleep(0.2)
157
- @subject.execution_interval.should == 3
158
- end
142
+ subject.execute
143
+ latch.wait(0.2)
159
144
 
160
- specify '#execution_interval is writeable' do
161
- @subject = TimerTask.new(timeout_interval: 1, execution_interval: 0.1) do |task|
162
- task.timeout_interval = 3
163
- end
164
- @subject.timeout_interval.should == 1
165
- @subject.timeout_interval = 2
166
- @subject.timeout_interval.should == 2
167
- @thread = Thread.new { @subject.run }
168
- sleep(0.2)
169
- @subject.timeout_interval.should == 3
145
+ subject.execution_interval.should == 3
146
+ subject.kill
147
+ end
148
+
149
+ specify '#execution_interval is writeable' do
150
+
151
+ latch = CountDownLatch.new(1)
152
+ subject = TimerTask.new(timeout_interval: 1, execution_interval: 0.1) do |task|
153
+ task.timeout_interval = 3
154
+ latch.count_down
170
155
  end
156
+
157
+ subject.timeout_interval.should == 1
158
+ subject.timeout_interval = 2
159
+ subject.timeout_interval.should == 2
160
+
161
+ subject.execute
162
+ latch.wait(0.2)
163
+
164
+ subject.timeout_interval.should == 3
165
+ subject.kill
171
166
  end
172
167
  end
173
168
 
174
169
  context 'execution' do
175
170
 
176
171
  it 'runs the block immediately when the :run_now option is true' do
177
- @expected = false
178
- @subject = TimerTask.run!(execution: 500, now: true){ @expected = true }
179
- sleep(0.1)
180
- @expected.should be_true
172
+ latch = CountDownLatch.new(1)
173
+ subject = TimerTask.execute(execution: 500, now: true){ latch.count_down }
174
+ latch.wait(0.1).should be_true
175
+ subject.kill
181
176
  end
182
177
 
183
178
  it 'waits for :execution_interval seconds when the :run_now option is false' do
184
- @expected = false
185
- @subject = TimerTask.run!(execution: 0.5, now: false){ @expected = true }
186
- @expected.should be_false
187
- sleep(1)
188
- @expected.should be_true
179
+ latch = CountDownLatch.new(1)
180
+ subject = TimerTask.execute(execution: 0.1, now: false){ latch.count_down }
181
+ latch.count.should eq 1
182
+ latch.wait(0.2).should be_true
183
+ subject.kill
189
184
  end
190
185
 
191
186
  it 'waits for :execution_interval seconds when the :run_now option is not given' do
192
- @expected = false
193
- @subject = TimerTask.run!(execution: 0.5){ @expected = true }
194
- @expected.should be_false
195
- sleep(1)
196
- @expected.should be_true
197
- end
198
-
199
- it 'yields to the execution block' do
200
- @expected = false
201
- @subject = TimerTask.run!(execution: 1){ @expected = true }
202
- sleep(2)
203
- @expected.should be_true
187
+ latch = CountDownLatch.new(1)
188
+ subject = TimerTask.execute(execution: 0.1, now: false){ latch.count_down }
189
+ latch.count.should eq 1
190
+ latch.wait(0.2).should be_true
191
+ subject.kill
204
192
  end
205
193
 
206
194
  it 'passes a "self" reference to the block as the sole argument' do
207
- @expected = nil
208
- @subject = TimerTask.new(execution_interval: 1, run_now: true) do |task|
209
- @expected = task
195
+ expected = nil
196
+ latch = CountDownLatch.new(1)
197
+ subject = TimerTask.new(execution_interval: 1, run_now: true) do |task|
198
+ expected = task
199
+ latch.sount_down
210
200
  end
211
- @thread = Thread.new { @subject.run }
212
- sleep(0.2)
213
- @expected.should eq @subject
214
- end
215
-
216
- it 'kills the worker thread if the timeout is reached' do
217
- # the after(:each) block will trigger this expectation
218
- Thread.should_receive(:kill).at_least(1).with(any_args())
219
- @subject = TimerTask.new(execution_interval: 0.5, timeout_interval: 0.5){ Thread.stop }
220
- @thread = Thread.new { @subject.run }
221
- sleep(1.5)
201
+ subject.execute
202
+ latch.wait(0.2)
203
+ expected.should eq subject
204
+ subject.kill
222
205
  end
223
206
  end
224
207
 
@@ -229,42 +212,45 @@ module Concurrent
229
212
  attr_reader :time
230
213
  attr_reader :value
231
214
  attr_reader :ex
215
+ attr_reader :latch
216
+ define_method(:initialize){ @latch = CountDownLatch.new(1) }
232
217
  define_method(:update) do |time, value, ex|
233
218
  @time = time
234
219
  @value = value
235
220
  @ex = ex
221
+ @latch.count_down
236
222
  end
237
223
  end.new
238
224
  end
239
225
 
240
226
  it 'notifies all observers on success' do
241
- task = TimerTask.new(run_now: true){ sleep(0.1); 42 }
242
- task.add_observer(observer)
243
- Thread.new{ task.run }
244
- sleep(1)
227
+ subject = TimerTask.new(execution: 0.1){ 42 }
228
+ subject.add_observer(observer)
229
+ subject.execute
230
+ observer.latch.wait(1)
245
231
  observer.value.should == 42
246
232
  observer.ex.should be_nil
247
- task.kill
233
+ subject.kill
248
234
  end
249
235
 
250
236
  it 'notifies all observers on timeout' do
251
- task = TimerTask.new(run_now: true, timeout: 1){ sleep }
252
- task.add_observer(observer)
253
- Thread.new{ task.run }
254
- sleep(2)
237
+ subject = TimerTask.new(execution: 0.1, timeout: 0.1){ sleep }
238
+ subject.add_observer(observer)
239
+ subject.execute
240
+ observer.latch.wait(1)
255
241
  observer.value.should be_nil
256
242
  observer.ex.should be_a(Concurrent::TimeoutError)
257
- task.kill
243
+ subject.kill
258
244
  end
259
245
 
260
246
  it 'notifies all observers on error' do
261
- task = TimerTask.new(run_now: true){ sleep(0.1); raise ArgumentError }
262
- task.add_observer(observer)
263
- Thread.new{ task.run }
264
- sleep(1)
247
+ subject = TimerTask.new(execution: 0.1){ raise ArgumentError }
248
+ subject.add_observer(observer)
249
+ subject.execute
250
+ observer.latch.wait(1)
265
251
  observer.value.should be_nil
266
252
  observer.ex.should be_a(ArgumentError)
267
- task.kill
253
+ subject.kill
268
254
  end
269
255
  end
270
256
  end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe '#timer' do
6
+
7
+ it 'raises an exception when no block given' do
8
+ expect {
9
+ Concurrent::timer(0.1)
10
+ }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ it 'raises an exception if the interval is less than 0 seconds' do
14
+ expect {
15
+ Concurrent::timer(-1){ :foo }
16
+ }.to raise_error(ArgumentError)
17
+ end
18
+
19
+ it 'executes the block after the given number of seconds' do
20
+ start = Time.now
21
+ expected = Concurrent::AtomicFixnum.new(0)
22
+ Concurrent::timer(0.5){ expected.increment }
23
+ expected.value.should eq 0
24
+ sleep(0.1)
25
+ expected.value.should eq 0
26
+ sleep(0.8)
27
+ expected.value.should eq 1
28
+ end
29
+
30
+ it 'suppresses exceptions thrown by the block' do
31
+ expect {
32
+ Concurrent::timer(0.5){ raise Exception }
33
+ }.to_not raise_error
34
+ end
35
+
36
+ it 'passes all arguments to the block' do
37
+ expected = nil
38
+ latch = CountDownLatch.new(1)
39
+ Concurrent::timer(0, 1, 2, 3) do |*args|
40
+ expected = args
41
+ latch.count_down
42
+ end
43
+ latch.wait(0.2)
44
+ expected.should eq [1, 2, 3]
45
+ end
46
+
47
+ it 'runs the task on the global timer pool' do
48
+ Concurrent.configuration.global_timer_set.should_receive(:post).with(0.1)
49
+ Concurrent::timer(0.1){ :foo }
50
+ end
51
+ end
52
+ end