concurrent-ruby 0.1.1.pre.3 → 0.1.1.pre.4
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -21
- data/README.md +275 -279
- data/lib/concurrent.rb +27 -28
- data/lib/concurrent/agent.rb +114 -108
- data/lib/concurrent/cached_thread_pool.rb +129 -130
- data/lib/concurrent/defer.rb +65 -67
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +93 -95
- data/lib/concurrent/fixed_thread_pool.rb +95 -89
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -47
- 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 -166
- data/lib/concurrent/reactor.rb +161 -162
- data/lib/concurrent/reactor/drb_async_demux.rb +74 -74
- data/lib/concurrent/reactor/tcp_sync_demux.rb +98 -98
- data/lib/concurrent/thread_pool.rb +76 -69
- data/lib/concurrent/utilities.rb +32 -34
- 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 +176 -176
- 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 -380
- 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 -253
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +184 -184
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -84
- 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 +54 -54
- data/spec/concurrent/obligation_shared.rb +135 -121
- data/spec/concurrent/promise_spec.rb +312 -305
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +12 -12
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +12 -12
- data/spec/concurrent/reactor_spec.rb +351 -10
- data/spec/concurrent/thread_pool_shared.rb +209 -210
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +44 -30
- metadata +11 -22
- data/lib/concurrent/smart_mutex.rb +0 -66
- data/spec/concurrent/smart_mutex_spec.rb +0 -234
@@ -1,12 +1,12 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class Reactor
|
5
|
-
|
6
|
-
describe DRbAsyncDemux do
|
7
|
-
|
8
|
-
pending
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Reactor
|
5
|
+
|
6
|
+
describe DRbAsyncDemux do
|
7
|
+
|
8
|
+
pending
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class Reactor
|
5
|
-
|
6
|
-
describe TcpSyncDemux do
|
7
|
-
|
8
|
-
pending
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
class Reactor
|
5
|
+
|
6
|
+
describe TcpSyncDemux do
|
7
|
+
|
8
|
+
pending
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,10 +1,351 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
describe Reactor do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Reactor do
|
6
|
+
|
7
|
+
let(:sync_demux) do
|
8
|
+
Class.new {
|
9
|
+
def initialize
|
10
|
+
@running = false
|
11
|
+
@queue = Queue.new
|
12
|
+
end
|
13
|
+
def start() @running = true; end
|
14
|
+
def stop
|
15
|
+
@queue.push(:stop)
|
16
|
+
@running = false
|
17
|
+
end
|
18
|
+
def stopped?() return ! @running; end
|
19
|
+
def accept()
|
20
|
+
event = @queue.pop
|
21
|
+
if event == :stop
|
22
|
+
return nil
|
23
|
+
else
|
24
|
+
return Reactor::EventContext.new(event)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def respond(result, message) return [result, message]; end
|
28
|
+
def close() nil; end
|
29
|
+
def send(event) @queue.push(event) end
|
30
|
+
}.new
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:async_demux) do
|
34
|
+
Class.new {
|
35
|
+
def initialize() @running = false; end
|
36
|
+
def start() @running = true; end
|
37
|
+
def stop() @running = false; end
|
38
|
+
def stopped?() return ! @running; end
|
39
|
+
def set_reactor(reactor) @reactor = reactor; end
|
40
|
+
def send(event) @reactor.handle(event); end
|
41
|
+
}.new
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#initialize' do
|
45
|
+
|
46
|
+
it 'raises an exception when the demux is not valid' do
|
47
|
+
lambda {
|
48
|
+
Reactor.new('bogus demux')
|
49
|
+
}.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'sets the initial state to not running' do
|
53
|
+
Reactor.new.should_not be_running
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#running?' do
|
58
|
+
|
59
|
+
it 'returns true when the reactor is running' do
|
60
|
+
reactor = Reactor.new
|
61
|
+
Thread.new{ reactor.start }
|
62
|
+
sleep(0.1)
|
63
|
+
reactor.should be_running
|
64
|
+
reactor.stop
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns true when the reactor is stopped' do
|
68
|
+
reactor = Reactor.new
|
69
|
+
Thread.new{ reactor.start }
|
70
|
+
sleep(0.1)
|
71
|
+
reactor.stop
|
72
|
+
sleep(0.1)
|
73
|
+
reactor.should_not be_running
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context '#add_handler' do
|
78
|
+
|
79
|
+
it 'raises an exception is the event name is reserved' do
|
80
|
+
reactor = Reactor.new
|
81
|
+
lambda {
|
82
|
+
reactor.add_handler(Reactor::RESERVED_EVENTS.first){ nil }
|
83
|
+
}.should raise_error(ArgumentError)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'raises an exception if no block is given' do
|
87
|
+
reactor = Reactor.new
|
88
|
+
lambda {
|
89
|
+
reactor.add_handler('no block given')
|
90
|
+
}.should raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'returns true if the handler is added' do
|
94
|
+
reactor = Reactor.new
|
95
|
+
reactor.add_handler('good'){ nil }.should be_true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context '#remove_handler' do
|
100
|
+
|
101
|
+
it 'returns true if the handler is found and removed' do
|
102
|
+
reactor = Reactor.new
|
103
|
+
reactor.add_handler('good'){ nil }
|
104
|
+
reactor.remove_handler('good').should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'returns false if the handler is not found' do
|
108
|
+
reactor = Reactor.new
|
109
|
+
reactor.remove_handler('not found').should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context '#stop_on_signal' do
|
114
|
+
|
115
|
+
it 'traps each valid signal' do
|
116
|
+
Signal.should_receive(:trap).with('USR1')
|
117
|
+
Signal.should_receive(:trap).with('USR2')
|
118
|
+
reactor = Reactor.new
|
119
|
+
reactor.stop_on_signal('USR1', 'USR2')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'raises an exception if given an invalid signal' do
|
123
|
+
if mri?
|
124
|
+
reactor = Reactor.new
|
125
|
+
lambda {
|
126
|
+
reactor.stop_on_signal('BOGUS')
|
127
|
+
}.should raise_error(ArgumentError)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'stops the reactor when it receives a trapped signal' do
|
132
|
+
reactor = Reactor.new
|
133
|
+
reactor.stop_on_signal('USR1')
|
134
|
+
reactor.should_receive(:stop).with(no_args())
|
135
|
+
Process.kill('USR1', Process.pid)
|
136
|
+
sleep(0.1)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context '#handle' do
|
141
|
+
|
142
|
+
it 'raises an exception if the demux is synchronous' do
|
143
|
+
reactor = Reactor.new(sync_demux)
|
144
|
+
lambda {
|
145
|
+
reactor.handle('event')
|
146
|
+
}.should raise_error(NotImplementedError)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns :stopped if the reactor is not running' do
|
150
|
+
reactor = Reactor.new
|
151
|
+
reactor.handle('event').first.should eq :stopped
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns :ok and the block result on success' do
|
155
|
+
reactor = Reactor.new
|
156
|
+
reactor.add_handler(:event){ 10 }
|
157
|
+
Thread.new{ reactor.start }
|
158
|
+
sleep(0.1)
|
159
|
+
result = reactor.handle(:event)
|
160
|
+
result.first.should eq :ok
|
161
|
+
result.last.should eq 10
|
162
|
+
reactor.stop
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'returns :ex and the exception on failure' do
|
166
|
+
reactor = Reactor.new
|
167
|
+
reactor.add_handler(:event){ raise StandardError }
|
168
|
+
Thread.new{ reactor.start }
|
169
|
+
sleep(0.1)
|
170
|
+
result = reactor.handle(:event)
|
171
|
+
result.first.should eq :ex
|
172
|
+
result.last.should be_a(StandardError)
|
173
|
+
reactor.stop
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'returns :noop when there is no handler' do
|
177
|
+
reactor = Reactor.new
|
178
|
+
Thread.new{ reactor.start }
|
179
|
+
sleep(0.1)
|
180
|
+
result = reactor.handle(:event)
|
181
|
+
result.first.should eq :noop
|
182
|
+
reactor.stop
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'triggers handlers added after the reactor is started' do
|
186
|
+
@expected = false
|
187
|
+
reactor = Reactor.new
|
188
|
+
Thread.new{ reactor.start }
|
189
|
+
sleep(0.1)
|
190
|
+
reactor.add_handler(:event){ @expected = true }
|
191
|
+
reactor.handle(:event)
|
192
|
+
@expected.should be_true
|
193
|
+
reactor.stop
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'does not trigger an event that was removed' do
|
197
|
+
@expected = false
|
198
|
+
reactor = Reactor.new
|
199
|
+
reactor.add_handler(:event){ @expected = true }
|
200
|
+
reactor.remove_handler(:event)
|
201
|
+
Thread.new{ reactor.start }
|
202
|
+
sleep(0.1)
|
203
|
+
reactor.handle(:event)
|
204
|
+
@expected.should be_false
|
205
|
+
reactor.stop
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context '#start' do
|
210
|
+
|
211
|
+
it 'raises an exception if the reactor is already running' do
|
212
|
+
reactor = Reactor.new
|
213
|
+
Thread.new{ reactor.start }
|
214
|
+
sleep(0.1)
|
215
|
+
lambda {
|
216
|
+
reactor.start
|
217
|
+
}.should raise_error(StandardError)
|
218
|
+
reactor.stop
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'starts the reactor if it is not running' do
|
222
|
+
reactor = Reactor.new(async_demux)
|
223
|
+
reactor.should_receive(:run_async).with(no_args())
|
224
|
+
Thread.new{ reactor.start }
|
225
|
+
sleep(0.1)
|
226
|
+
reactor.should be_running
|
227
|
+
reactor.stop
|
228
|
+
|
229
|
+
reactor = Reactor.new(sync_demux)
|
230
|
+
reactor.should_receive(:run_sync).with(no_args())
|
231
|
+
Thread.new{ reactor.start }
|
232
|
+
sleep(0.1)
|
233
|
+
reactor.should be_running
|
234
|
+
reactor.stop
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context '#stop' do
|
239
|
+
|
240
|
+
it 'returns if the reactor is not running' do
|
241
|
+
reactor = Reactor.new
|
242
|
+
reactor.stop.should be_true
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'stops the reactor when running and synchronous' do
|
246
|
+
reactor = Reactor.new(sync_demux)
|
247
|
+
Thread.new{ sleep(0.1); reactor.stop }
|
248
|
+
Thread.pass
|
249
|
+
reactor.start
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'stops the reactor when running and asynchronous' do
|
253
|
+
reactor = Reactor.new(async_demux)
|
254
|
+
Thread.new{ sleep(0.1); reactor.stop }
|
255
|
+
Thread.pass
|
256
|
+
reactor.start
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'stops the reactor when running without a demux' do
|
260
|
+
reactor = Reactor.new
|
261
|
+
Thread.new{ sleep(0.1); reactor.stop }
|
262
|
+
Thread.pass
|
263
|
+
reactor.start
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
specify 'synchronous demultiplexing' do
|
268
|
+
|
269
|
+
demux = sync_demux
|
270
|
+
reactor = Concurrent::Reactor.new(demux)
|
271
|
+
|
272
|
+
reactor.should_not be_running
|
273
|
+
|
274
|
+
reactor.add_handler(:foo){ 'Foo' }
|
275
|
+
reactor.add_handler(:bar){ 'Bar' }
|
276
|
+
reactor.add_handler(:baz){ 'Baz' }
|
277
|
+
reactor.add_handler(:fubar){ raise StandardError.new('Boom!') }
|
278
|
+
|
279
|
+
reactor.stop_on_signal('USR1')
|
280
|
+
|
281
|
+
demux.should_receive(:respond).with(:ok, 'Foo')
|
282
|
+
demux.send(:foo)
|
283
|
+
|
284
|
+
t = Thread.new do
|
285
|
+
reactor.start
|
286
|
+
end
|
287
|
+
t.abort_on_exception = true
|
288
|
+
sleep(0.1)
|
289
|
+
|
290
|
+
reactor.should be_running
|
291
|
+
|
292
|
+
demux.should_receive(:respond).with(:ok, 'Bar')
|
293
|
+
demux.should_receive(:respond).with(:ok, 'Baz')
|
294
|
+
demux.should_receive(:respond).with(:noop, anything())
|
295
|
+
demux.should_receive(:respond).with(:ex, anything())
|
296
|
+
|
297
|
+
demux.send(:bar)
|
298
|
+
demux.send(:baz)
|
299
|
+
demux.send(:bogus)
|
300
|
+
demux.send(:fubar)
|
301
|
+
|
302
|
+
reactor.should be_running
|
303
|
+
|
304
|
+
Process.kill('USR1', Process.pid)
|
305
|
+
sleep(0.1)
|
306
|
+
|
307
|
+
demux.should_not_receive(:respond).with(:foo, anything())
|
308
|
+
demux.send(:foo)
|
309
|
+
reactor.should_not be_running
|
310
|
+
end
|
311
|
+
|
312
|
+
specify 'asynchronous demultiplexing' do
|
313
|
+
|
314
|
+
demux = async_demux
|
315
|
+
reactor = Concurrent::Reactor.new(demux)
|
316
|
+
|
317
|
+
reactor.should_not be_running
|
318
|
+
|
319
|
+
reactor.add_handler(:foo){ 'Foo' }
|
320
|
+
reactor.add_handler(:bar){ 'Bar' }
|
321
|
+
reactor.add_handler(:baz){ 'Baz' }
|
322
|
+
reactor.add_handler(:fubar){ raise StandardError.new('Boom!') }
|
323
|
+
|
324
|
+
reactor.stop_on_signal('USR2')
|
325
|
+
|
326
|
+
demux.send(:foo).first.should eq :stopped
|
327
|
+
|
328
|
+
t = Thread.new do
|
329
|
+
reactor.start
|
330
|
+
end
|
331
|
+
t.abort_on_exception = true
|
332
|
+
sleep(0.1)
|
333
|
+
|
334
|
+
reactor.should be_running
|
335
|
+
|
336
|
+
demux.send(:foo).should eq [:ok, 'Foo']
|
337
|
+
demux.send(:bar).should eq [:ok, 'Bar']
|
338
|
+
demux.send(:baz).should eq [:ok, 'Baz']
|
339
|
+
demux.send(:bogus).first.should eq :noop
|
340
|
+
demux.send(:fubar).first.should eq :ex
|
341
|
+
|
342
|
+
reactor.should be_running
|
343
|
+
|
344
|
+
Process.kill('USR2', Process.pid)
|
345
|
+
sleep(0.1)
|
346
|
+
|
347
|
+
demux.send(:foo).first.should eq :stopped
|
348
|
+
reactor.should_not be_running
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
@@ -1,210 +1,209 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
share_examples_for 'Thread Pool' do
|
6
|
-
|
7
|
-
context '#running?' do
|
8
|
-
|
9
|
-
it 'returns true when the thread pool is running' do
|
10
|
-
subject.should be_running
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'returns false when the thread pool is shutting down' do
|
14
|
-
subject.post{ sleep(1) }
|
15
|
-
subject.shutdown
|
16
|
-
subject.should_not be_running
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'returns false when the thread pool is shutdown' do
|
20
|
-
subject.shutdown
|
21
|
-
subject.should_not be_running
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'returns false when the thread pool is killed' do
|
25
|
-
subject.shutdown
|
26
|
-
subject.should_not be_running
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context '#shutdown?' do
|
31
|
-
|
32
|
-
it 'returns true if #shutdown is complete' do
|
33
|
-
subject.shutdown
|
34
|
-
sleep(0.1)
|
35
|
-
subject.should be_shutdown
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'returns false when running' do
|
39
|
-
subject.should_not be_shutdown
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context '#killed?' do
|
44
|
-
|
45
|
-
it 'returns true if tasks were killed at shutdown' do
|
46
|
-
subject.post{ sleep(1) }
|
47
|
-
subject.kill
|
48
|
-
subject.should be_killed
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'returns false when running' do
|
52
|
-
subject.should_not be_killed
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context '#shutdown' do
|
57
|
-
|
58
|
-
it 'stops accepting new tasks' do
|
59
|
-
subject.post{ sleep(1) }
|
60
|
-
subject.shutdown
|
61
|
-
@expected = false
|
62
|
-
subject.post{ @expected = true }.should be_false
|
63
|
-
sleep(1)
|
64
|
-
@expected.should be_false
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'allows in-progress tasks to complete' do
|
68
|
-
@expected = false
|
69
|
-
subject.post{ sleep(0.5); @expected = true }
|
70
|
-
subject.shutdown
|
71
|
-
sleep(1)
|
72
|
-
@expected.should be_true
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'allows pending tasks to complete' do
|
76
|
-
@expected = false
|
77
|
-
subject.post{ sleep(0.2) }
|
78
|
-
subject.post{ sleep(0.2); @expected = true }
|
79
|
-
subject.shutdown
|
80
|
-
sleep(1)
|
81
|
-
@expected.should be_true
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'allows threads to exit normally' do
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
subject.
|
96
|
-
|
97
|
-
@expected =
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
@expected =
|
105
|
-
subject.
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
subject.post{ sleep(0.5) }
|
114
|
-
subject.
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
subject.
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
subject.
|
130
|
-
subject.
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
subject.
|
136
|
-
subject.
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
subject.
|
142
|
-
subject.
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
pool
|
170
|
-
pool.
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
subject.
|
181
|
-
subject.
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
pool
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
subject.
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
@expected =
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
share_examples_for 'Thread Pool' do
|
6
|
+
|
7
|
+
context '#running?' do
|
8
|
+
|
9
|
+
it 'returns true when the thread pool is running' do
|
10
|
+
subject.should be_running
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns false when the thread pool is shutting down' do
|
14
|
+
subject.post{ sleep(1) }
|
15
|
+
subject.shutdown
|
16
|
+
subject.should_not be_running
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns false when the thread pool is shutdown' do
|
20
|
+
subject.shutdown
|
21
|
+
subject.should_not be_running
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns false when the thread pool is killed' do
|
25
|
+
subject.shutdown
|
26
|
+
subject.should_not be_running
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#shutdown?' do
|
31
|
+
|
32
|
+
it 'returns true if #shutdown is complete' do
|
33
|
+
subject.shutdown
|
34
|
+
sleep(0.1)
|
35
|
+
subject.should be_shutdown
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns false when running' do
|
39
|
+
subject.should_not be_shutdown
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context '#killed?' do
|
44
|
+
|
45
|
+
it 'returns true if tasks were killed at shutdown' do
|
46
|
+
subject.post{ sleep(1) }
|
47
|
+
subject.kill
|
48
|
+
subject.should be_killed
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns false when running' do
|
52
|
+
subject.should_not be_killed
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context '#shutdown' do
|
57
|
+
|
58
|
+
it 'stops accepting new tasks' do
|
59
|
+
subject.post{ sleep(1) }
|
60
|
+
subject.shutdown
|
61
|
+
@expected = false
|
62
|
+
subject.post{ @expected = true }.should be_false
|
63
|
+
sleep(1)
|
64
|
+
@expected.should be_false
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'allows in-progress tasks to complete' do
|
68
|
+
@expected = false
|
69
|
+
subject.post{ sleep(0.5); @expected = true }
|
70
|
+
subject.shutdown
|
71
|
+
sleep(1)
|
72
|
+
@expected.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'allows pending tasks to complete' do
|
76
|
+
@expected = false
|
77
|
+
subject.post{ sleep(0.2) }
|
78
|
+
subject.post{ sleep(0.2); @expected = true }
|
79
|
+
subject.shutdown
|
80
|
+
sleep(1)
|
81
|
+
@expected.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'allows threads to exit normally' do
|
85
|
+
subject.shutdown
|
86
|
+
sleep(1)
|
87
|
+
subject.status.should be_empty
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#kill' do
|
92
|
+
|
93
|
+
it 'stops accepting new tasks' do
|
94
|
+
subject.post{ sleep(1) }
|
95
|
+
subject.kill
|
96
|
+
@expected = false
|
97
|
+
subject.post{ @expected = true }.should be_false
|
98
|
+
sleep(1)
|
99
|
+
@expected.should be_false
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'attempts to kill all in-progress tasks' do
|
103
|
+
@expected = false
|
104
|
+
subject.post{ sleep(1); @expected = true }
|
105
|
+
subject.kill
|
106
|
+
sleep(1)
|
107
|
+
@expected.should be_false
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'rejects all pending tasks' do
|
111
|
+
@expected = false
|
112
|
+
subject.post{ sleep(0.5) }
|
113
|
+
subject.post{ sleep(0.5); @expected = true }
|
114
|
+
subject.kill
|
115
|
+
sleep(1)
|
116
|
+
@expected.should be_false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context '#wait_for_termination' do
|
121
|
+
|
122
|
+
it 'immediately returns true after shutdown has complete' do
|
123
|
+
subject.shutdown
|
124
|
+
subject.wait_for_termination.should be_true
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'blocks indefinitely when timeout it nil' do
|
128
|
+
subject.post{ sleep(1) }
|
129
|
+
subject.shutdown
|
130
|
+
subject.wait_for_termination(nil).should be_true
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'returns true when shutdown sucessfully completes before timeout' do
|
134
|
+
subject.post{ sleep(0.5) }
|
135
|
+
subject.shutdown
|
136
|
+
subject.wait_for_termination(1).should be_true
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns false when shutdown fails to complete before timeout' do
|
140
|
+
subject.post{ sleep(1) }
|
141
|
+
subject.shutdown
|
142
|
+
subject.wait_for_termination(0.5).should be_false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context '#post' do
|
147
|
+
|
148
|
+
it 'raises an exception if no block is given' do
|
149
|
+
lambda {
|
150
|
+
subject.post
|
151
|
+
}.should raise_error(ArgumentError)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns true when the block is added to the queue' do
|
155
|
+
subject.post{ nil }.should be_true
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'calls the block with the given arguments' do
|
159
|
+
@expected = nil
|
160
|
+
subject.post(1, 2, 3)do |a, b, c|
|
161
|
+
@expected = a + b + c
|
162
|
+
end
|
163
|
+
sleep(0.1)
|
164
|
+
@expected.should eq 6
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'rejects the block while shutting down' do
|
168
|
+
pool = FixedThreadPool.new(5)
|
169
|
+
pool.post{ sleep(1) }
|
170
|
+
pool.shutdown
|
171
|
+
@expected = nil
|
172
|
+
pool.post(1, 2, 3)do |a, b, c|
|
173
|
+
@expected = a + b + c
|
174
|
+
end
|
175
|
+
@expected.should be_nil
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns false while shutting down' do
|
179
|
+
subject.post{ sleep(1) }
|
180
|
+
subject.shutdown
|
181
|
+
subject.post{ nil }.should be_false
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'rejects the block once shutdown' do
|
185
|
+
pool = FixedThreadPool.new(5)
|
186
|
+
pool.shutdown
|
187
|
+
@expected = nil
|
188
|
+
pool.post(1, 2, 3)do |a, b, c|
|
189
|
+
@expected = a + b + c
|
190
|
+
end
|
191
|
+
@expected.should be_nil
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'returns false once shutdown' do
|
195
|
+
subject.post{ nil }
|
196
|
+
subject.shutdown
|
197
|
+
sleep(0.1)
|
198
|
+
subject.post{ nil }.should be_false
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'aliases #<<' do
|
202
|
+
@expected = false
|
203
|
+
subject << proc { @expected = true }
|
204
|
+
sleep(0.1)
|
205
|
+
@expected.should be_true
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|