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