concurrent-ruby 0.1.1.pre.3 → 0.1.1.pre.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -21
  3. data/README.md +275 -279
  4. data/lib/concurrent.rb +27 -28
  5. data/lib/concurrent/agent.rb +114 -108
  6. data/lib/concurrent/cached_thread_pool.rb +129 -130
  7. data/lib/concurrent/defer.rb +65 -67
  8. data/lib/concurrent/event.rb +60 -60
  9. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  10. data/lib/concurrent/executor.rb +93 -95
  11. data/lib/concurrent/fixed_thread_pool.rb +95 -89
  12. data/lib/concurrent/functions.rb +120 -120
  13. data/lib/concurrent/future.rb +42 -47
  14. data/lib/concurrent/global_thread_pool.rb +16 -16
  15. data/lib/concurrent/goroutine.rb +29 -29
  16. data/lib/concurrent/null_thread_pool.rb +22 -22
  17. data/lib/concurrent/obligation.rb +67 -67
  18. data/lib/concurrent/promise.rb +174 -166
  19. data/lib/concurrent/reactor.rb +161 -162
  20. data/lib/concurrent/reactor/drb_async_demux.rb +74 -74
  21. data/lib/concurrent/reactor/tcp_sync_demux.rb +98 -98
  22. data/lib/concurrent/thread_pool.rb +76 -69
  23. data/lib/concurrent/utilities.rb +32 -34
  24. data/lib/concurrent/version.rb +3 -3
  25. data/lib/concurrent_ruby.rb +1 -1
  26. data/md/agent.md +123 -123
  27. data/md/defer.md +174 -174
  28. data/md/event.md +32 -32
  29. data/md/executor.md +176 -176
  30. data/md/future.md +83 -83
  31. data/md/goroutine.md +52 -52
  32. data/md/obligation.md +32 -32
  33. data/md/promise.md +227 -227
  34. data/md/thread_pool.md +224 -224
  35. data/spec/concurrent/agent_spec.rb +386 -380
  36. data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
  37. data/spec/concurrent/defer_spec.rb +195 -195
  38. data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -253
  39. data/spec/concurrent/event_spec.rb +134 -134
  40. data/spec/concurrent/executor_spec.rb +184 -184
  41. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -84
  42. data/spec/concurrent/functions_spec.rb +217 -217
  43. data/spec/concurrent/future_spec.rb +108 -108
  44. data/spec/concurrent/global_thread_pool_spec.rb +38 -38
  45. data/spec/concurrent/goroutine_spec.rb +67 -67
  46. data/spec/concurrent/null_thread_pool_spec.rb +54 -54
  47. data/spec/concurrent/obligation_shared.rb +135 -121
  48. data/spec/concurrent/promise_spec.rb +312 -305
  49. data/spec/concurrent/reactor/drb_async_demux_spec.rb +12 -12
  50. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +12 -12
  51. data/spec/concurrent/reactor_spec.rb +351 -10
  52. data/spec/concurrent/thread_pool_shared.rb +209 -210
  53. data/spec/concurrent/utilities_spec.rb +74 -74
  54. data/spec/spec_helper.rb +44 -30
  55. metadata +11 -22
  56. data/lib/concurrent/smart_mutex.rb +0 -66
  57. 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
- pending
8
-
9
- end
10
- end
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
- pool = FixedThreadPool.new(5)
86
- pool.shutdown
87
- sleep(1)
88
- pool.status.should be_empty
89
- end
90
- end
91
-
92
- context '#kill' do
93
-
94
- it 'stops accepting new tasks' do
95
- subject.post{ sleep(1) }
96
- subject.kill
97
- @expected = false
98
- subject.post{ @expected = true }.should be_false
99
- sleep(1)
100
- @expected.should be_false
101
- end
102
-
103
- it 'attempts to kill all in-progress tasks' do
104
- @expected = false
105
- subject.post{ sleep(1); @expected = true }
106
- subject.kill
107
- sleep(1)
108
- @expected.should be_false
109
- end
110
-
111
- it 'rejects all pending tasks' do
112
- @expected = false
113
- subject.post{ sleep(0.5) }
114
- subject.post{ sleep(0.5); @expected = true }
115
- subject.kill
116
- sleep(1)
117
- @expected.should be_false
118
- end
119
- end
120
-
121
- context '#wait_for_termination' do
122
-
123
- it 'immediately returns true after shutdown has complete' do
124
- subject.shutdown
125
- subject.wait_for_termination.should be_true
126
- end
127
-
128
- it 'blocks indefinitely when timeout it nil' do
129
- subject.post{ sleep(1) }
130
- subject.shutdown
131
- subject.wait_for_termination(nil).should be_true
132
- end
133
-
134
- it 'returns true when shutdown sucessfully completes before timeout' do
135
- subject.post{ sleep(0.5) }
136
- subject.shutdown
137
- subject.wait_for_termination(1).should be_true
138
- end
139
-
140
- it 'returns false when shutdown fails to complete before timeout' do
141
- subject.post{ sleep(1) }
142
- subject.shutdown
143
- subject.wait_for_termination(0.5).should be_false
144
- end
145
- end
146
-
147
- context '#post' do
148
-
149
- it 'raises an exception if no block is given' do
150
- lambda {
151
- subject.post
152
- }.should raise_error(ArgumentError)
153
- end
154
-
155
- it 'returns true when the block is added to the queue' do
156
- subject.post{ nil }.should be_true
157
- end
158
-
159
- it 'calls the block with the given arguments' do
160
- @expected = nil
161
- subject.post(1, 2, 3)do |a, b, c|
162
- @expected = a + b + c
163
- end
164
- sleep(0.1)
165
- @expected.should eq 6
166
- end
167
-
168
- it 'rejects the block while shutting down' do
169
- pool = FixedThreadPool.new(5)
170
- pool.post{ sleep(1) }
171
- pool.shutdown
172
- @expected = nil
173
- pool.post(1, 2, 3)do |a, b, c|
174
- @expected = a + b + c
175
- end
176
- @expected.should be_nil
177
- end
178
-
179
- it 'returns false while shutting down' do
180
- subject.post{ sleep(1) }
181
- subject.shutdown
182
- subject.post{ nil }.should be_false
183
- end
184
-
185
- it 'rejects the block once shutdown' do
186
- pool = FixedThreadPool.new(5)
187
- pool.shutdown
188
- @expected = nil
189
- pool.post(1, 2, 3)do |a, b, c|
190
- @expected = a + b + c
191
- end
192
- @expected.should be_nil
193
- end
194
-
195
- it 'returns false once shutdown' do
196
- subject.post{ nil }
197
- subject.shutdown
198
- sleep(0.1)
199
- subject.post{ nil }.should be_false
200
- end
201
-
202
- it 'aliases #<<' do
203
- @expected = false
204
- subject << proc { @expected = true }
205
- sleep(0.1)
206
- @expected.should be_true
207
- end
208
- end
209
- end
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