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