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