concurrent-ruby 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +21 -21
- data/README.md +275 -275
- data/lib/concurrent.rb +28 -28
- data/lib/concurrent/agent.rb +114 -114
- data/lib/concurrent/cached_thread_pool.rb +131 -129
- data/lib/concurrent/defer.rb +65 -65
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +96 -95
- data/lib/concurrent/fixed_thread_pool.rb +99 -95
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -42
- data/lib/concurrent/global_thread_pool.rb +16 -16
- data/lib/concurrent/goroutine.rb +29 -29
- data/lib/concurrent/null_thread_pool.rb +22 -22
- data/lib/concurrent/obligation.rb +67 -67
- data/lib/concurrent/promise.rb +174 -174
- data/lib/concurrent/reactor.rb +166 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
- data/lib/concurrent/supervisor.rb +105 -100
- data/lib/concurrent/thread_pool.rb +76 -76
- data/lib/concurrent/utilities.rb +32 -32
- data/lib/concurrent/version.rb +3 -3
- data/lib/concurrent_ruby.rb +1 -1
- data/md/agent.md +123 -123
- data/md/defer.md +174 -174
- data/md/event.md +32 -32
- data/md/executor.md +187 -187
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +227 -227
- data/md/thread_pool.md +224 -224
- data/spec/concurrent/agent_spec.rb +386 -386
- data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
- data/spec/concurrent/defer_spec.rb +195 -195
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +200 -200
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
- data/spec/concurrent/functions_spec.rb +217 -217
- data/spec/concurrent/future_spec.rb +108 -108
- data/spec/concurrent/global_thread_pool_spec.rb +38 -38
- data/spec/concurrent/goroutine_spec.rb +67 -67
- data/spec/concurrent/null_thread_pool_spec.rb +57 -54
- data/spec/concurrent/obligation_shared.rb +132 -132
- data/spec/concurrent/promise_spec.rb +312 -312
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
- data/spec/concurrent/reactor_spec.rb +364 -364
- data/spec/concurrent/supervisor_spec.rb +269 -258
- data/spec/concurrent/thread_pool_shared.rb +204 -204
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +32 -32
- metadata +20 -16
- checksums.yaml +0 -7
@@ -1,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
|