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,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require_relative 'dereferenceable_shared'
3
3
  require_relative 'obligation_shared'
4
+ require_relative 'observable_shared'
4
5
 
5
6
  module Concurrent
6
7
 
@@ -54,6 +55,17 @@ module Concurrent
54
55
  end
55
56
 
56
57
  it_should_behave_like :dereferenceable
58
+
59
+ # observable
60
+
61
+ subject{ Future.new{ nil } }
62
+
63
+ def trigger_observable(observable)
64
+ observable.execute
65
+ sleep(0.1)
66
+ end
67
+
68
+ it_should_behave_like :observable
57
69
  end
58
70
 
59
71
  context 'subclassing' do
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require_relative 'dereferenceable_shared'
3
3
  require_relative 'obligation_shared'
4
+ require_relative 'observable_shared'
4
5
 
5
6
  module Concurrent
6
7
 
@@ -56,10 +57,19 @@ module Concurrent
56
57
 
57
58
  def execute_dereferenceable(subject)
58
59
  subject.set('value')
59
- sleep(0.1)
60
60
  end
61
61
 
62
62
  it_should_behave_like :dereferenceable
63
+
64
+ # observable
65
+
66
+ subject{ IVar.new }
67
+
68
+ def trigger_observable(observable)
69
+ observable.set('value')
70
+ end
71
+
72
+ it_should_behave_like :observable
63
73
  end
64
74
 
65
75
  context '#initialize' do
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ share_examples_for :observable do
4
+
5
+ let(:observer_set) do
6
+ subject.instance_variable_get(:@observers)
7
+ end
8
+
9
+ let(:observer_class) do
10
+ Class.new do
11
+ def initialize(&block)
12
+ @block = block
13
+ end
14
+ def update(*args)
15
+ @block.call(*args) if @block
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:observer){ observer_class.new }
21
+
22
+ let!(:observer_func){ :notify }
23
+
24
+ let(:observer_with_func_class) do
25
+ Class.new do
26
+ def initialize(&block)
27
+ @block = block
28
+ end
29
+ def notify(*args)
30
+ @block.call(*args) if @block
31
+ end
32
+ end
33
+ end
34
+
35
+ let(:observer_with_func){ observer_with_func_class.new }
36
+
37
+ context '#add_observer' do
38
+
39
+ it 'adds an observer if called before first notification' do
40
+ observer_set.should_receive(:add_observer).with(any_args)
41
+ subject.add_observer(observer)
42
+ end
43
+
44
+ it 'adds an observer with :func if called before first notification' do
45
+ observer_set.should_receive(:add_observer).with(observer_with_func, :notify)
46
+ subject.add_observer(observer_with_func, observer_func)
47
+ end
48
+
49
+ it 'creates an observer from a block if called before first notification' do
50
+ block = proc{ nil }
51
+ observer_set.should_receive(:add_observer).with(any_args)
52
+ subject.add_observer(&block)
53
+ end
54
+
55
+ it 'raises an exception if not given an observer or a block' do
56
+ expect {
57
+ subject.add_observer
58
+ }.to raise_error(ArgumentError)
59
+ end
60
+
61
+ it 'raises an exception when given both an observer and a block' do
62
+ expect {
63
+ subject.add_observer(observer){ nil }
64
+ }.to raise_error(ArgumentError)
65
+ end
66
+ end
67
+
68
+ context '#delete_observer' do
69
+
70
+ it 'deletes the given observer if called before first notification' do
71
+ subject.count_observers.should eq 0
72
+ subject.add_observer(observer)
73
+ subject.count_observers.should eq 1
74
+ subject.delete_observer(observer)
75
+ subject.count_observers.should eq 0
76
+ end
77
+
78
+ it 'returns the removed observer if found in the observer set' do
79
+ subject.add_observer(observer)
80
+ subject.delete_observer(observer).should eq observer
81
+ end
82
+
83
+ it 'returns the given observer even when not found in the observer set' do
84
+ subject.delete_observer(observer).should eq observer
85
+ end
86
+ end
87
+
88
+ context '#delete_observers' do
89
+
90
+ it 'deletes all observers when called before first notification' do
91
+ 5.times{ subject.add_observer(observer_class.new) }
92
+ subject.count_observers.should eq 5
93
+ subject.delete_observers
94
+ subject.count_observers.should eq 0
95
+ end
96
+
97
+ it 'returns self' do
98
+ subject.delete_observers.should eq subject
99
+ end
100
+ end
101
+
102
+ context '#count_observers' do
103
+
104
+ it 'returns zero for a new observable object' do
105
+ subject.count_observers.should eq 0
106
+ end
107
+
108
+ it 'returns a count of registered observers if called before first notification' do
109
+ 5.times{ subject.add_observer(observer_class.new) }
110
+ subject.count_observers.should eq 5
111
+ end
112
+
113
+ it 'returns zero after #delete_observers has been called' do
114
+ 5.times{ subject.add_observer(observer_class.new) }
115
+ subject.delete_observers
116
+ subject.count_observers.should eq 0
117
+ end
118
+ end
119
+
120
+ context 'first notification' do
121
+
122
+ it 'calls the #update method on all observers without a specified :func' do
123
+ latch = Concurrent::CountDownLatch.new(5)
124
+ 5.times do
125
+ subject.add_observer(observer_class.new{ latch.count_down })
126
+ end
127
+ trigger_observable(subject)
128
+ latch.count.should eq 0
129
+ end
130
+
131
+ it 'calls the appropriate function on all observers which specified a :func' do
132
+ latch = Concurrent::CountDownLatch.new(5)
133
+ 5.times do
134
+ obs = observer_with_func_class.new{ latch.count_down }
135
+ subject.add_observer(obs, observer_func)
136
+ end
137
+ trigger_observable(subject)
138
+ latch.count.should eq 0
139
+ end
140
+
141
+ it 'calls the proc for all observers added as a block' do
142
+ latch = Concurrent::CountDownLatch.new(5)
143
+ 5.times do
144
+ subject.add_observer{ latch.count_down }
145
+ end
146
+ trigger_observable(subject)
147
+ latch.count.should eq 0
148
+ end
149
+
150
+ it 'does not notify any observers removed with #delete_observer' do
151
+ latch = Concurrent::CountDownLatch.new(5)
152
+
153
+ obs = observer_class.new{ latch.count_down }
154
+ subject.add_observer(obs)
155
+ subject.delete_observer(obs)
156
+
157
+ trigger_observable(subject)
158
+ latch.count.should eq 5
159
+ end
160
+
161
+ it 'does not notify any observers after #delete_observers called' do
162
+ latch = Concurrent::CountDownLatch.new(5)
163
+ 5.times do
164
+ subject.add_observer(observer_class.new{ latch.count_down })
165
+ end
166
+
167
+ subject.delete_observers
168
+
169
+ trigger_observable(subject)
170
+ latch.count.should eq 5
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Observable do
6
+
7
+ let (:described_class) do
8
+ Class.new do
9
+ include Concurrent::Observable
10
+ public :observers, :observers=
11
+ end
12
+ end
13
+
14
+ let(:observer_set) { double(:observer_set) }
15
+ subject { described_class.new }
16
+
17
+ before(:each) do
18
+ subject.observers = observer_set
19
+ end
20
+
21
+ it 'does not initialize set by by default' do
22
+ described_class.new.observers.should be_nil
23
+ end
24
+
25
+ it 'uses the given observer set' do
26
+ expected = CopyOnWriteObserverSet.new
27
+ subject.observers = expected
28
+ subject.observers.should eql expected
29
+ end
30
+
31
+ it 'delegates #add_observer' do
32
+ observer_set.should_receive(:add_observer).with(:observer)
33
+ subject.add_observer(:observer)
34
+ end
35
+
36
+ it 'delegates #delete_observer' do
37
+ observer_set.should_receive(:delete_observer).with(:observer)
38
+ subject.delete_observer(:observer)
39
+ end
40
+
41
+ it 'delegates #delete_observers' do
42
+ observer_set.should_receive(:delete_observers).with(no_args)
43
+ subject.delete_observers
44
+ end
45
+
46
+ it 'delegates #count_observers' do
47
+ observer_set.should_receive(:count_observers).with(no_args)
48
+ subject.count_observers
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe OptionsParser do
6
+
7
+ let(:executor){ ImmediateExecutor.new }
8
+
9
+ let(:task_pool){ ImmediateExecutor.new }
10
+ let(:operation_pool){ ImmediateExecutor.new }
11
+
12
+ context '#get_executor_from' do
13
+
14
+ it 'returns the given :executor' do
15
+ OptionsParser::get_executor_from(executor: executor).should eq executor
16
+ end
17
+
18
+ it 'returns the global operation pool when :operation is true' do
19
+ Concurrent.configuration.should_receive(:global_operation_pool).
20
+ and_return(:operation_pool)
21
+ OptionsParser::get_executor_from(operation: true)
22
+ end
23
+
24
+ it 'returns the global task pool when :operation is false' do
25
+ Concurrent.configuration.should_receive(:global_task_pool).
26
+ and_return(:task_pool)
27
+ OptionsParser::get_executor_from(operation: false)
28
+ end
29
+
30
+ it 'returns the global operation pool when :task is false' do
31
+ Concurrent.configuration.should_receive(:global_operation_pool).
32
+ and_return(:operation_pool)
33
+ OptionsParser::get_executor_from(task: false)
34
+ end
35
+
36
+ it 'returns the global task pool when :task is true' do
37
+ Concurrent.configuration.should_receive(:global_task_pool).
38
+ and_return(:task_pool)
39
+ OptionsParser::get_executor_from(task: true)
40
+ end
41
+
42
+ it 'returns the global task pool when :executor is nil' do
43
+ Concurrent.configuration.should_receive(:global_task_pool).
44
+ and_return(:task_pool)
45
+ OptionsParser::get_executor_from(executor: nil)
46
+ end
47
+
48
+ it 'returns the global task pool when no option is given' do
49
+ Concurrent.configuration.should_receive(:global_task_pool).
50
+ and_return(:task_pool)
51
+ OptionsParser::get_executor_from
52
+ end
53
+
54
+ specify ':executor overrides :operation' do
55
+ OptionsParser::get_executor_from(executor: executor, operation: true).
56
+ should eq executor
57
+ end
58
+
59
+ specify ':executor overrides :task' do
60
+ OptionsParser::get_executor_from(executor: executor, task: true).
61
+ should eq executor
62
+ end
63
+
64
+ specify ':operation overrides :task' do
65
+ Concurrent.configuration.should_receive(:global_operation_pool).
66
+ and_return(:operation_pool)
67
+ OptionsParser::get_executor_from(operation: true, task: true)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -55,7 +55,13 @@ share_examples_for :runnable do
55
55
  subject.should be_running
56
56
  end
57
57
 
58
+ it 'returns false when first created' do
59
+ subject.should_not be_running
60
+ end
61
+
58
62
  it 'returns false when not running' do
63
+ subject.stop
64
+ sleep(0.1)
59
65
  subject.should_not be_running
60
66
  end
61
67
  end
@@ -2,6 +2,7 @@ require 'spec_helper'
2
2
  require 'timecop'
3
3
  require_relative 'dereferenceable_shared'
4
4
  require_relative 'obligation_shared'
5
+ require_relative 'observable_shared'
5
6
 
6
7
  module Concurrent
7
8
 
@@ -19,11 +20,19 @@ module Concurrent
19
20
  end
20
21
 
21
22
  let(:fulfilled_subject) do
22
- ScheduledTask.new(0.1){ fulfilled_value }.execute.tap{ sleep(0.2) }
23
+ latch = Concurrent::CountDownLatch.new(1)
24
+ task = ScheduledTask.new(0.1){ latch.count_down; fulfilled_value }.execute
25
+ latch.wait(1)
26
+ sleep(0.1)
27
+ task
23
28
  end
24
29
 
25
30
  let(:rejected_subject) do
26
- ScheduledTask.new(0.1){ raise rejected_reason }.execute.tap{ sleep(0.2) }
31
+ latch = Concurrent::CountDownLatch.new(1)
32
+ task = ScheduledTask.new(0.1){ latch.count_down; raise rejected_reason }.execute
33
+ latch.wait(1)
34
+ sleep(0.1)
35
+ task
27
36
  end
28
37
 
29
38
  it_should_behave_like :obligation
@@ -44,6 +53,17 @@ module Concurrent
44
53
  end
45
54
 
46
55
  it_should_behave_like :dereferenceable
56
+
57
+ # observable
58
+
59
+ subject{ ScheduledTask.new(0.1){ nil } }
60
+
61
+ def trigger_observable(observable)
62
+ observable.execute
63
+ sleep(0.2)
64
+ end
65
+
66
+ it_should_behave_like :observable
47
67
  end
48
68
 
49
69
  context '#initialize' do
@@ -65,19 +85,19 @@ module Concurrent
65
85
  it 'raises an exception when seconds is less than zero' do
66
86
  expect {
67
87
  ScheduledTask.new(-1){ nil }
68
- }.to raise_error(ScheduledTask::SchedulingError)
88
+ }.to raise_error(ArgumentError)
69
89
  end
70
90
 
71
91
  it 'raises an exception when schedule time is in the past' do
72
92
  expect {
73
93
  ScheduledTask.new(Time.now - 60){ nil }
74
- }.to raise_error(ScheduledTask::SchedulingError)
94
+ }.to raise_error(ArgumentError)
75
95
  end
76
96
 
77
97
  it 'raises an exception when no block given' do
78
98
  expect {
79
99
  ScheduledTask.new(1)
80
- }.to raise_error(ScheduledTask::SchedulingError)
100
+ }.to raise_error(ArgumentError)
81
101
  end
82
102
 
83
103
  it 'sets the initial state to :unscheduled' do
@@ -121,16 +141,10 @@ module Concurrent
121
141
  Timecop.travel(60)
122
142
  expect {
123
143
  task.execute
124
- }.to raise_error(ScheduledTask::SchedulingError)
144
+ }.to raise_error(ArgumentError)
125
145
  end
126
146
  end
127
147
 
128
- it 'spawns a new thread when a block was given on construction' do
129
- Thread.should_receive(:new).with(any_args)
130
- task = ScheduledTask.new(1){ nil }
131
- task.execute
132
- end
133
-
134
148
  it 'sets the sate to :pending' do
135
149
  task = ScheduledTask.new(1){ nil }
136
150
  task.execute
@@ -153,7 +167,7 @@ module Concurrent
153
167
  it 'passes the block to the new ScheduledTask' do
154
168
  @expected = false
155
169
  task = ScheduledTask.execute(0.1){ @expected = true }
156
- sleep(0.2)
170
+ task.value(1)
157
171
  @expected.should be_true
158
172
  end
159
173
 
@@ -169,37 +183,39 @@ module Concurrent
169
183
 
170
184
  it 'returns false if the task has already been performed' do
171
185
  task = ScheduledTask.new(0.1){ 42 }.execute
172
- sleep(0.2)
186
+ task.value(1)
173
187
  task.cancel.should be_false
174
188
  end
175
189
 
176
190
  it 'returns false if the task is already in progress' do
177
- task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
178
- sleep(0.2)
191
+ latch = Concurrent::CountDownLatch.new(1)
192
+ task = ScheduledTask.new(0.1) {
193
+ latch.count_down
194
+ sleep(1)
195
+ }.execute
196
+ latch.wait(1)
179
197
  task.cancel.should be_false
180
198
  end
181
199
 
182
200
  it 'cancels the task if it has not yet scheduled' do
183
- @expected = true
184
- task = ScheduledTask.new(0.1){ @expected = false }
201
+ latch = Concurrent::CountDownLatch.new(1)
202
+ task = ScheduledTask.new(0.1){ latch.count_down }
185
203
  task.cancel
186
204
  task.execute
187
- sleep(0.5)
188
- @expected.should be_true
205
+ latch.wait(0.3).should be_false
189
206
  end
190
207
 
191
208
 
192
209
  it 'cancels the task if it has not yet started' do
193
- @expected = true
194
- task = ScheduledTask.new(0.3){ @expected = false }.execute
210
+ latch = Concurrent::CountDownLatch.new(1)
211
+ task = ScheduledTask.new(0.3){ latch.count_down }.execute
195
212
  sleep(0.1)
196
213
  task.cancel
197
- sleep(0.5)
198
- @expected.should be_true
214
+ latch.wait(0.5).should be_false
199
215
  end
200
216
 
201
217
  it 'returns true on success' do
202
- task = ScheduledTask.new(0.3){ @expected = false }.execute
218
+ task = ScheduledTask.new(10){ nil }.execute
203
219
  sleep(0.1)
204
220
  task.cancel.should be_true
205
221
  end
@@ -215,8 +231,12 @@ module Concurrent
215
231
  context 'execution' do
216
232
 
217
233
  it 'sets the state to :in_progress when the task is running' do
218
- task = ScheduledTask.new(0.1){ sleep(1); 42 }.execute
219
- sleep(0.2)
234
+ latch = Concurrent::CountDownLatch.new(1)
235
+ task = ScheduledTask.new(0.1) {
236
+ latch.count_down
237
+ sleep(1)
238
+ }.execute
239
+ latch.wait(1)
220
240
  task.should be_in_progress
221
241
  end
222
242
  end
@@ -228,10 +248,15 @@ module Concurrent
228
248
  attr_reader :value
229
249
  attr_reader :reason
230
250
  attr_reader :count
231
- define_method(:update) do |time, value, reason|
251
+ attr_reader :latch
252
+ def initialize
253
+ @latch = Concurrent::CountDownLatch.new(1)
254
+ end
255
+ def update(time, value, reason)
232
256
  @count = @count.to_i + 1
233
257
  @value = value
234
258
  @reason = reason
259
+ @latch.count_down
235
260
  end
236
261
  end
237
262
  end
@@ -262,22 +287,20 @@ module Concurrent
262
287
 
263
288
  it 'returns false for an observer added once :fulfilled' do
264
289
  task = ScheduledTask.new(0.1){ 42 }.execute
265
- sleep(0.2)
290
+ task.value(1)
266
291
  task.add_observer(observer).should be_false
267
292
  end
268
293
 
269
294
  it 'returns false for an observer added once :rejected' do
270
295
  task = ScheduledTask.new(0.1){ raise StandardError }.execute
271
- sleep(0.2)
296
+ task.value(0.2)
272
297
  task.add_observer(observer).should be_false
273
298
  end
274
299
 
275
300
  it 'notifies all observers on fulfillment' do
276
301
  task = ScheduledTask.new(0.1){ 42 }.execute
277
302
  task.add_observer(observer)
278
- sleep(0.2)
279
- task.value.should == 42
280
- task.reason.should be_nil
303
+ observer.latch.wait(1)
281
304
  observer.value.should == 42
282
305
  observer.reason.should be_nil
283
306
  end
@@ -285,9 +308,7 @@ module Concurrent
285
308
  it 'notifies all observers on rejection' do
286
309
  task = ScheduledTask.new(0.1){ raise StandardError }.execute
287
310
  task.add_observer(observer)
288
- sleep(0.2)
289
- task.value.should be_nil
290
- task.reason.should be_a(StandardError)
311
+ observer.latch.wait(1)
291
312
  observer.value.should be_nil
292
313
  observer.reason.should be_a(StandardError)
293
314
  end
@@ -295,7 +316,7 @@ module Concurrent
295
316
  it 'does not notify an observer added after fulfillment' do
296
317
  observer.should_not_receive(:update).with(any_args)
297
318
  task = ScheduledTask.new(0.1){ 42 }.execute
298
- sleep(0.2)
319
+ task.value(1)
299
320
  task.add_observer(observer)
300
321
  sleep(0.1)
301
322
  end
@@ -303,7 +324,7 @@ module Concurrent
303
324
  it 'does not notify an observer added after rejection' do
304
325
  observer.should_not_receive(:update).with(any_args)
305
326
  task = ScheduledTask.new(0.1){ raise StandardError }.execute
306
- sleep(0.2)
327
+ task.value(1)
307
328
  task.add_observer(observer)
308
329
  sleep(0.1)
309
330
  end
@@ -313,9 +334,8 @@ module Concurrent
313
334
  task = ScheduledTask.new(0.1){ 42 }.execute
314
335
  task.cancel
315
336
  task.add_observer(observer)
316
- sleep(0.2)
337
+ task.value(1)
317
338
  end
318
-
319
339
  end
320
340
  end
321
341
  end