concurrent-ruby 0.2.0 → 0.2.1

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