concurrent-ruby 0.2.0 → 0.2.1
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.
- data/LICENSE +21 -21
- data/README.md +275 -275
- data/lib/concurrent.rb +28 -28
- data/lib/concurrent/agent.rb +114 -114
- data/lib/concurrent/cached_thread_pool.rb +131 -129
- data/lib/concurrent/defer.rb +65 -65
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +96 -95
- data/lib/concurrent/fixed_thread_pool.rb +99 -95
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -42
- data/lib/concurrent/global_thread_pool.rb +16 -16
- data/lib/concurrent/goroutine.rb +29 -29
- data/lib/concurrent/null_thread_pool.rb +22 -22
- data/lib/concurrent/obligation.rb +67 -67
- data/lib/concurrent/promise.rb +174 -174
- data/lib/concurrent/reactor.rb +166 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
- data/lib/concurrent/supervisor.rb +105 -100
- data/lib/concurrent/thread_pool.rb +76 -76
- data/lib/concurrent/utilities.rb +32 -32
- data/lib/concurrent/version.rb +3 -3
- data/lib/concurrent_ruby.rb +1 -1
- data/md/agent.md +123 -123
- data/md/defer.md +174 -174
- data/md/event.md +32 -32
- data/md/executor.md +187 -187
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +227 -227
- data/md/thread_pool.md +224 -224
- data/spec/concurrent/agent_spec.rb +386 -386
- data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
- data/spec/concurrent/defer_spec.rb +195 -195
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +200 -200
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
- data/spec/concurrent/functions_spec.rb +217 -217
- data/spec/concurrent/future_spec.rb +108 -108
- data/spec/concurrent/global_thread_pool_spec.rb +38 -38
- data/spec/concurrent/goroutine_spec.rb +67 -67
- data/spec/concurrent/null_thread_pool_spec.rb +57 -54
- data/spec/concurrent/obligation_shared.rb +132 -132
- data/spec/concurrent/promise_spec.rb +312 -312
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
- data/spec/concurrent/reactor_spec.rb +364 -364
- data/spec/concurrent/supervisor_spec.rb +269 -258
- data/spec/concurrent/thread_pool_shared.rb +204 -204
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +32 -32
- metadata +20 -16
- checksums.yaml +0 -7
@@ -1,258 +1,269 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
describe Supervisor do
|
6
|
-
|
7
|
-
let(:worker_class) do
|
8
|
-
Class.new {
|
9
|
-
behavior(:runnable)
|
10
|
-
def run() return true; end
|
11
|
-
def stop() return true; end
|
12
|
-
def running?() return true; end
|
13
|
-
}
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:worker){ worker_class.new }
|
17
|
-
|
18
|
-
subject{ Supervisor.new }
|
19
|
-
|
20
|
-
after(:each) do
|
21
|
-
subject.stop
|
22
|
-
end
|
23
|
-
|
24
|
-
context '#initialize' do
|
25
|
-
|
26
|
-
it 'sets the initial length to zero' do
|
27
|
-
supervisor = Supervisor.new
|
28
|
-
supervisor.length.should == 0
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'sets the initial length to one when a worker is provided' do
|
32
|
-
supervisor = Supervisor.new(worker: worker)
|
33
|
-
supervisor.length.should == 1
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'sets the initial state to stopped' do
|
37
|
-
supervisor = Supervisor.new
|
38
|
-
supervisor.should_not be_running
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sets the monitor interval when given' do
|
42
|
-
supervisor = Supervisor.new
|
43
|
-
supervisor.monitor_interval.should == Supervisor::DEFAULT_MONITOR_INTERVAL
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'sets the monitor interval to the default when not given' do
|
47
|
-
supervisor = Supervisor.new(monitor_interval: 5)
|
48
|
-
supervisor.monitor_interval.should == 5
|
49
|
-
|
50
|
-
supervisor = Supervisor.new(monitor: 10)
|
51
|
-
supervisor.monitor_interval.should == 10
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'run' do
|
56
|
-
|
57
|
-
it 'runs the monitor' do
|
58
|
-
subject.should_receive(:monitor).with(no_args()).at_least(1).times
|
59
|
-
t = Thread.new{ subject.run }
|
60
|
-
sleep(0.1)
|
61
|
-
subject.stop
|
62
|
-
Thread.kill(t) unless t.nil?
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'calls #run on all workers' do
|
66
|
-
supervisor = Supervisor.new(worker: worker)
|
67
|
-
# must stub AFTER adding or else #add_worker will reject
|
68
|
-
worker.should_receive(:run).with(no_args())
|
69
|
-
t = Thread.new{ supervisor.run }
|
70
|
-
sleep(0.1)
|
71
|
-
supervisor.stop
|
72
|
-
Thread.kill(t)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'sets the state to running' do
|
76
|
-
t = Thread.new{ subject.run }
|
77
|
-
sleep(0.1)
|
78
|
-
subject.should be_running
|
79
|
-
subject.stop
|
80
|
-
Thread.kill(t)
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'raises an exception when already running' do
|
84
|
-
@thread = nil
|
85
|
-
subject.run!
|
86
|
-
lambda {
|
87
|
-
@thread = Thread.new{ subject.run }
|
88
|
-
@thread.abort_on_exception = true
|
89
|
-
sleep(0.1)
|
90
|
-
}.should raise_error(StandardError)
|
91
|
-
subject.stop
|
92
|
-
Thread.kill(@thread) unless @thread.nil?
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context '#run!' do
|
97
|
-
|
98
|
-
it 'runs the monitor thread' do
|
99
|
-
Thread.
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
subject.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
workers.
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
subject.
|
146
|
-
subject.
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
subject.
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
it '
|
191
|
-
subject.add_worker(
|
192
|
-
subject.length.should ==
|
193
|
-
end
|
194
|
-
|
195
|
-
it '
|
196
|
-
subject.
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
it '
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
supervisor.
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Supervisor do
|
6
|
+
|
7
|
+
let(:worker_class) do
|
8
|
+
Class.new {
|
9
|
+
behavior(:runnable)
|
10
|
+
def run() return true; end
|
11
|
+
def stop() return true; end
|
12
|
+
def running?() return true; end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:worker){ worker_class.new }
|
17
|
+
|
18
|
+
subject{ Supervisor.new }
|
19
|
+
|
20
|
+
after(:each) do
|
21
|
+
subject.stop
|
22
|
+
end
|
23
|
+
|
24
|
+
context '#initialize' do
|
25
|
+
|
26
|
+
it 'sets the initial length to zero' do
|
27
|
+
supervisor = Supervisor.new
|
28
|
+
supervisor.length.should == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets the initial length to one when a worker is provided' do
|
32
|
+
supervisor = Supervisor.new(worker: worker)
|
33
|
+
supervisor.length.should == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sets the initial state to stopped' do
|
37
|
+
supervisor = Supervisor.new
|
38
|
+
supervisor.should_not be_running
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'sets the monitor interval when given' do
|
42
|
+
supervisor = Supervisor.new
|
43
|
+
supervisor.monitor_interval.should == Supervisor::DEFAULT_MONITOR_INTERVAL
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sets the monitor interval to the default when not given' do
|
47
|
+
supervisor = Supervisor.new(monitor_interval: 5)
|
48
|
+
supervisor.monitor_interval.should == 5
|
49
|
+
|
50
|
+
supervisor = Supervisor.new(monitor: 10)
|
51
|
+
supervisor.monitor_interval.should == 10
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'run' do
|
56
|
+
|
57
|
+
it 'runs the monitor' do
|
58
|
+
subject.should_receive(:monitor).with(no_args()).at_least(1).times
|
59
|
+
t = Thread.new{ subject.run }
|
60
|
+
sleep(0.1)
|
61
|
+
subject.stop
|
62
|
+
Thread.kill(t) unless t.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'calls #run on all workers' do
|
66
|
+
supervisor = Supervisor.new(worker: worker)
|
67
|
+
# must stub AFTER adding or else #add_worker will reject
|
68
|
+
worker.should_receive(:run).with(no_args())
|
69
|
+
t = Thread.new{ supervisor.run }
|
70
|
+
sleep(0.1)
|
71
|
+
supervisor.stop
|
72
|
+
Thread.kill(t)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'sets the state to running' do
|
76
|
+
t = Thread.new{ subject.run }
|
77
|
+
sleep(0.1)
|
78
|
+
subject.should be_running
|
79
|
+
subject.stop
|
80
|
+
Thread.kill(t)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'raises an exception when already running' do
|
84
|
+
@thread = nil
|
85
|
+
subject.run!
|
86
|
+
lambda {
|
87
|
+
@thread = Thread.new{ subject.run }
|
88
|
+
@thread.abort_on_exception = true
|
89
|
+
sleep(0.1)
|
90
|
+
}.should raise_error(StandardError)
|
91
|
+
subject.stop
|
92
|
+
Thread.kill(@thread) unless @thread.nil?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context '#run!' do
|
97
|
+
|
98
|
+
it 'runs the monitor thread' do
|
99
|
+
thread = Thread.new{ nil }
|
100
|
+
Thread.should_receive(:new).with(no_args()).and_return(thread)
|
101
|
+
subject.run!
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'calls #run on all workers' do
|
105
|
+
supervisor = Supervisor.new(worker: worker)
|
106
|
+
# must stub AFTER adding or else #add_worker will reject
|
107
|
+
worker.should_receive(:run).with(no_args())
|
108
|
+
supervisor.run!
|
109
|
+
sleep(0.1)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'sets the state to running' do
|
113
|
+
subject.run!
|
114
|
+
subject.should be_running
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'raises an exception when already running' do
|
118
|
+
subject.run!
|
119
|
+
lambda {
|
120
|
+
subject.run!
|
121
|
+
}.should raise_error(StandardError)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context '#stop' do
|
126
|
+
|
127
|
+
it 'stops the monitor thread' do
|
128
|
+
Thread.should_receive(:kill).with(anything())
|
129
|
+
subject.run!
|
130
|
+
sleep(0.1)
|
131
|
+
subject.stop
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'calls #stop on all workers' do
|
135
|
+
workers = (1..3).collect{ worker_class.new }
|
136
|
+
workers.each{|worker| subject.add_worker(worker)}
|
137
|
+
# must stub AFTER adding or else #add_worker will reject
|
138
|
+
workers.each{|worker| worker.should_receive(:stop).with(no_args())}
|
139
|
+
subject.run!
|
140
|
+
sleep(0.1)
|
141
|
+
subject.stop
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'sets the state to stopped' do
|
145
|
+
subject.run!
|
146
|
+
subject.stop
|
147
|
+
subject.should_not be_running
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'returns true immediately when already stopped' do
|
151
|
+
subject.stop.should be_true
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'unblocks a thread blocked by #run and exits normally' do
|
155
|
+
supervisor = Supervisor.new(monitor: 0.1)
|
156
|
+
@thread = Thread.new{ sleep(0.5); supervisor.stop }
|
157
|
+
sleep(0.1)
|
158
|
+
lambda {
|
159
|
+
Timeout::timeout(1){ supervisor.run }
|
160
|
+
}.should_not raise_error
|
161
|
+
Thread.kill(@thread) unless @thread.nil?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context '#running?' do
|
166
|
+
|
167
|
+
it 'returns true when running' do
|
168
|
+
subject.run!
|
169
|
+
subject.should be_running
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'returns false when stopped' do
|
173
|
+
subject.run!
|
174
|
+
subject.stop
|
175
|
+
subject.should_not be_running
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context '#length' do
|
180
|
+
|
181
|
+
it 'returns a count of attached workers' do
|
182
|
+
workers = (1..3).collect{ worker.dup }
|
183
|
+
workers.each{|worker| subject.add_worker(worker)}
|
184
|
+
subject.length.should == 3
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context '#add_worker' do
|
189
|
+
|
190
|
+
it 'adds the worker when stopped' do
|
191
|
+
subject.add_worker(worker)
|
192
|
+
subject.length.should == 1
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'rejects the worker when running' do
|
196
|
+
subject.run!
|
197
|
+
subject.add_worker(worker)
|
198
|
+
subject.length.should == 0
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'rejects a worker without the :runnable behavior' do
|
202
|
+
subject.add_worker('bogus worker')
|
203
|
+
subject.length.should == 0
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'returns true when a worker is accepted' do
|
207
|
+
subject.add_worker(worker).should be_true
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'returns false when a worker is not accepted' do
|
211
|
+
subject.add_worker('bogus worker').should be_false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'supervision' do
|
216
|
+
|
217
|
+
it 'reruns any worker that stops' do
|
218
|
+
worker = Class.new(worker_class){
|
219
|
+
def run() sleep(0.2); end
|
220
|
+
}.new
|
221
|
+
|
222
|
+
supervisor = Supervisor.new(worker: worker, monitor: 0.1)
|
223
|
+
supervisor.add_worker(worker)
|
224
|
+
# must stub AFTER adding or else #add_worker will reject
|
225
|
+
worker.should_receive(:run).with(no_args()).at_least(2).times
|
226
|
+
supervisor.run!
|
227
|
+
sleep(1)
|
228
|
+
supervisor.stop
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'reruns any dead threads' do
|
232
|
+
worker = Class.new(worker_class){
|
233
|
+
def run() raise StandardError; end
|
234
|
+
}.new
|
235
|
+
|
236
|
+
supervisor = Supervisor.new(worker: worker, monitor: 0.1)
|
237
|
+
supervisor.add_worker(worker)
|
238
|
+
# must stub AFTER adding or else #add_worker will reject
|
239
|
+
worker.should_receive(:run).with(no_args()).at_least(2).times
|
240
|
+
supervisor.run!
|
241
|
+
sleep(1)
|
242
|
+
supervisor.stop
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'supervisor tree' do
|
247
|
+
|
248
|
+
specify do
|
249
|
+
s1 = Supervisor.new(monitor: 0.1)
|
250
|
+
s2 = Supervisor.new(monitor: 0.1)
|
251
|
+
s3 = Supervisor.new(monitor: 0.1)
|
252
|
+
|
253
|
+
workers = (1..3).collect{ worker_class.new }
|
254
|
+
workers.each{|worker| s3.add_worker(worker)}
|
255
|
+
# must stub AFTER adding or else #add_worker will reject
|
256
|
+
workers.each{|worker| worker.should_receive(:run).with(no_args())}
|
257
|
+
workers.each{|worker| worker.should_receive(:stop).with(no_args())}
|
258
|
+
|
259
|
+
s1.add_worker(s2)
|
260
|
+
s2.add_worker(s3)
|
261
|
+
|
262
|
+
s1.run!
|
263
|
+
sleep(0.1)
|
264
|
+
s1.stop
|
265
|
+
sleep(0.1)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|