concurrent-ruby 0.2.2 → 0.3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -42
  3. data/lib/concurrent.rb +5 -6
  4. data/lib/concurrent/agent.rb +29 -33
  5. data/lib/concurrent/cached_thread_pool.rb +26 -105
  6. data/lib/concurrent/channel.rb +94 -0
  7. data/lib/concurrent/event.rb +8 -17
  8. data/lib/concurrent/executor.rb +68 -72
  9. data/lib/concurrent/fixed_thread_pool.rb +15 -83
  10. data/lib/concurrent/functions.rb +7 -22
  11. data/lib/concurrent/future.rb +29 -9
  12. data/lib/concurrent/null_thread_pool.rb +5 -2
  13. data/lib/concurrent/obligation.rb +6 -16
  14. data/lib/concurrent/promise.rb +9 -10
  15. data/lib/concurrent/runnable.rb +103 -0
  16. data/lib/concurrent/supervisor.rb +271 -44
  17. data/lib/concurrent/thread_pool.rb +112 -39
  18. data/lib/concurrent/version.rb +1 -1
  19. data/md/executor.md +9 -3
  20. data/md/goroutine.md +11 -9
  21. data/md/reactor.md +32 -0
  22. data/md/supervisor.md +43 -0
  23. data/spec/concurrent/agent_spec.rb +128 -51
  24. data/spec/concurrent/cached_thread_pool_spec.rb +33 -47
  25. data/spec/concurrent/channel_spec.rb +446 -0
  26. data/spec/concurrent/event_machine_defer_proxy_spec.rb +3 -1
  27. data/spec/concurrent/event_spec.rb +0 -19
  28. data/spec/concurrent/executor_spec.rb +167 -119
  29. data/spec/concurrent/fixed_thread_pool_spec.rb +40 -30
  30. data/spec/concurrent/functions_spec.rb +0 -20
  31. data/spec/concurrent/future_spec.rb +88 -0
  32. data/spec/concurrent/null_thread_pool_spec.rb +23 -2
  33. data/spec/concurrent/obligation_shared.rb +0 -5
  34. data/spec/concurrent/promise_spec.rb +9 -10
  35. data/spec/concurrent/runnable_shared.rb +62 -0
  36. data/spec/concurrent/runnable_spec.rb +233 -0
  37. data/spec/concurrent/supervisor_spec.rb +912 -47
  38. data/spec/concurrent/thread_pool_shared.rb +18 -31
  39. data/spec/spec_helper.rb +10 -3
  40. metadata +17 -23
  41. data/lib/concurrent/defer.rb +0 -65
  42. data/lib/concurrent/reactor.rb +0 -166
  43. data/lib/concurrent/reactor/drb_async_demux.rb +0 -83
  44. data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -131
  45. data/lib/concurrent/utilities.rb +0 -32
  46. data/md/defer.md +0 -174
  47. data/spec/concurrent/defer_spec.rb +0 -199
  48. data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -196
  49. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -410
  50. data/spec/concurrent/reactor_spec.rb +0 -364
  51. data/spec/concurrent/utilities_spec.rb +0 -74
@@ -1,196 +0,0 @@
1
- require 'spec_helper'
2
- require 'faker'
3
-
4
- module Concurrent
5
- class Reactor
6
-
7
- describe DRbAsyncDemux, not_on_travis: true do
8
-
9
- subject{ DRbAsyncDemux.new }
10
-
11
- after(:each) do
12
- DRb.stop_service
13
- end
14
-
15
- def post_event(demux, event, *args)
16
- there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
17
- there.send(event, *args)
18
- end
19
-
20
- context '#initialize' do
21
-
22
- it 'sets the initial state to :stopped' do
23
- subject.should_not be_running
24
- end
25
-
26
- it 'uses the given URI' do
27
- uri = 'druby://concurrent-ruby.com:4242'
28
- demux = DRbAsyncDemux.new(uri: uri)
29
- demux.uri.should eq uri
30
- end
31
-
32
- it 'uses the default URI when none given' do
33
- demux = DRbAsyncDemux.new
34
- demux.uri.should eq DRbAsyncDemux::DEFAULT_URI
35
- end
36
-
37
- it 'uses the given ACL' do
38
- acl = %w[deny all]
39
- ACL.should_receive(:new).with(acl).and_return(acl)
40
- demux = DRbAsyncDemux.new(acl: acl)
41
- demux.acl.should eq acl
42
- end
43
-
44
- it 'uses the default ACL when given' do
45
- acl = DRbAsyncDemux::DEFAULT_ACL
46
- ACL.should_receive(:new).with(acl).and_return(acl)
47
- demux = DRbAsyncDemux.new
48
- demux.acl.should eq acl
49
- end
50
- end
51
-
52
- context '#run' do
53
-
54
- it 'raises an exception if already runed' do
55
- subject.run
56
-
57
- lambda {
58
- subject.run
59
- }.should raise_error(StandardError)
60
- end
61
-
62
- it 'installs the ACL' do
63
- acl = %w[deny all]
64
- ACL.should_receive(:new).once.with(acl).and_return(acl)
65
- DRb.should_receive(:install_acl).once.with(acl)
66
- demux = DRbAsyncDemux.new(acl: acl)
67
- reactor = Concurrent::Reactor.new(demux)
68
- Thread.new{ reactor.run }
69
- sleep(0.1)
70
- reactor.stop
71
- end
72
-
73
- it 'runs DRb' do
74
- uri = DRbAsyncDemux::DEFAULT_URI
75
- DRb.should_receive(:start_service).with(uri, anything())
76
- demux = DRbAsyncDemux.new(uri: uri)
77
- reactor = Concurrent::Reactor.new(demux)
78
- Thread.new{ reactor.run }
79
- sleep(0.1)
80
- reactor.stop
81
- end
82
- end
83
-
84
- context '#stop' do
85
-
86
- it 'stops DRb' do
87
- DRb.should_receive(:stop_service).at_least(1).times
88
- subject.run
89
- sleep(0.1)
90
- subject.stop
91
- end
92
- end
93
-
94
- context '#running?' do
95
-
96
- it 'returns false when stopped' do
97
- subject.run
98
- sleep(0.1)
99
- subject.stop
100
- sleep(0.1)
101
- subject.should_not be_running
102
- end
103
-
104
- it 'returns true when running' do
105
- subject.run
106
- sleep(0.1)
107
- subject.should be_running
108
- end
109
- end
110
-
111
- context '#set_reactor' do
112
-
113
- it 'raises an exception when given an invalid reactor' do
114
- lambda {
115
- subject.set_reactor(Concurrent::Reactor.new)
116
- }.should_not raise_error
117
-
118
- lambda {
119
- subject.set_reactor('bogus')
120
- }.should raise_error(ArgumentError)
121
- end
122
- end
123
-
124
- context 'event handling' do
125
-
126
- it 'should post events to the reactor' do
127
- demux = subject
128
- reactor = Concurrent::Reactor.new(demux)
129
- reactor.add_handler(:foo){ nil }
130
- reactor.should_receive(:handle).with(:foo, 1,2,3).and_return([:ok, nil])
131
-
132
- Thread.new { reactor.run }
133
- sleep(0.1)
134
-
135
- post_event(demux, :foo, 1, 2, 3)
136
-
137
- reactor.stop
138
- sleep(0.1)
139
- end
140
- end
141
-
142
- specify 'integration', not_on_travis: true do
143
-
144
- # server
145
- demux = Concurrent::Reactor::DRbAsyncDemux.new
146
- reactor = Concurrent::Reactor.new(demux)
147
-
148
- reactor.add_handler(:echo) {|message| message }
149
- reactor.add_handler(:error) {|message| raise StandardError.new(message) }
150
- reactor.add_handler(:unknown) {|message| message }
151
- reactor.add_handler(:abend) {|message| message }
152
-
153
- t = Thread.new { reactor.run }
154
- sleep(0.1)
155
-
156
- # client
157
- there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
158
-
159
- # test :ok
160
- 10.times do
161
- message = Faker::Company.bs
162
- echo = there.echo(message)
163
- echo.should eq message
164
- end
165
-
166
- # test :ex
167
- lambda {
168
- echo = there.error('error')
169
- }.should raise_error(StandardError, 'error')
170
-
171
- # test :noop
172
- lambda {
173
- echo = there.bogus('bogus')
174
- }.should raise_error(NoMethodError)
175
-
176
- # test unknown respone code
177
- reactor.should_receive(:handle).with(:unknown).and_return([:unknown, nil])
178
- lambda {
179
- echo = there.unknown
180
- }.should raise_error(DRb::DRbError)
181
-
182
- # test handler error
183
- reactor.should_receive(:handle).with(:abend).and_raise(ArgumentError)
184
- lambda {
185
- echo = there.abend
186
- }.should raise_error(DRb::DRbRemoteError)
187
-
188
- # cleanup
189
- reactor.stop
190
- sleep(0.1)
191
- Thread.kill(t)
192
- sleep(0.1)
193
- end
194
- end
195
- end
196
- end
@@ -1,410 +0,0 @@
1
- require 'spec_helper'
2
- require 'faker'
3
-
4
- module Concurrent
5
- class Reactor
6
-
7
- describe TcpSyncDemux, not_on_travis: true do
8
-
9
- subject do
10
- @subject = TcpSyncDemux.new(port: 2000 + rand(8000))
11
- end
12
-
13
- after(:each) do
14
- @subject.stop unless @subject.nil?
15
- end
16
-
17
- context '#initialize' do
18
-
19
- it 'sets the initial state to :stopped' do
20
- subject.should_not be_running
21
- end
22
-
23
- it 'raises an exception if already runed' do
24
- subject.run
25
-
26
- lambda {
27
- subject.run
28
- }.should raise_error(StandardError)
29
- end
30
-
31
- it 'uses the given host' do
32
- demux = TcpSyncDemux.new(host: 'www.foobar.com')
33
- demux.host.should eq 'www.foobar.com'
34
- demux.stop
35
- end
36
-
37
- it 'uses the default host when none is given' do
38
- demux = TcpSyncDemux.new
39
- demux.host.should eq TcpSyncDemux::DEFAULT_HOST
40
- demux.stop
41
- end
42
-
43
- it 'uses the given port' do
44
- demux = TcpSyncDemux.new(port: 4242)
45
- demux.port.should eq 4242
46
- demux.stop
47
- end
48
-
49
- it 'uses the default port when none is given' do
50
- demux = TcpSyncDemux.new
51
- demux.port.should eq TcpSyncDemux::DEFAULT_PORT
52
- demux.stop
53
- end
54
-
55
- it 'uses the given ACL' do
56
- acl = %w[deny all]
57
- ACL.should_receive(:new).with(acl).and_return(acl)
58
- demux = TcpSyncDemux.new(acl: acl)
59
- demux.acl.should eq acl
60
- demux.stop
61
- end
62
-
63
- it 'uses the default ACL when given' do
64
- acl = TcpSyncDemux::DEFAULT_ACL
65
- ACL.should_receive(:new).with(acl).and_return(acl)
66
- demux = TcpSyncDemux.new
67
- demux.acl.should eq acl
68
- demux.stop
69
- end
70
- end
71
-
72
- context '#run' do
73
-
74
- it 'creates a new TCP server' do
75
- TCPServer.should_receive(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
76
- subject.run
77
- end
78
-
79
- it 'returns true on success' do
80
- TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
81
- subject.run.should be_true
82
- end
83
-
84
- it 'returns false on failure' do
85
- TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything()) \
86
- .and_raise(StandardError)
87
- subject.run.should be_false
88
- end
89
-
90
- it 'raises an exception when already running' do
91
- subject.run
92
- lambda {
93
- subject.run
94
- }.should raise_error
95
- end
96
- end
97
-
98
- context '#stop' do
99
-
100
- let(:server){ double('tcp server') }
101
- let(:socket){ double('tcp socket') }
102
-
103
- before(:each) do
104
- socket.stub(:close).with(no_args())
105
- server.stub(:close).with(no_args())
106
- subject.run
107
- subject.instance_variable_set(:@socket, socket)
108
- subject.instance_variable_set(:@server, server)
109
- end
110
-
111
- it 'immediately returns true when not running' do
112
- socket.should_not_receive(:close)
113
- server.should_not_receive(:close)
114
- demux = TcpSyncDemux.new
115
- demux.stop.should be_true
116
- end
117
-
118
- it 'closes the socket' do
119
- socket.should_receive(:close).with(no_args())
120
- subject.stop
121
- end
122
-
123
- it 'closes the TCP server' do
124
- server.should_receive(:close).with(no_args())
125
- subject.stop
126
- end
127
-
128
- it 'is supresses socket close exceptions' do
129
- socket.should_receive(:close).and_raise(SocketError)
130
- lambda {
131
- subject.stop
132
- }.should_not raise_error
133
- end
134
-
135
- it 'supresses server close exceptions' do
136
- server.should_receive(:close).and_raise(SocketError)
137
- lambda {
138
- subject.stop
139
- }.should_not raise_error
140
- end
141
- end
142
-
143
- context '#running?' do
144
-
145
- it 'returns false when stopped' do
146
- subject.run
147
- sleep(0.1)
148
- subject.stop
149
- sleep(0.1)
150
- subject.should_not be_running
151
- end
152
-
153
- it 'returns true when running' do
154
- subject.run
155
- sleep(0.1)
156
- subject.should be_running
157
- end
158
- end
159
-
160
- context '#reset' do
161
-
162
- it 'closes the demux' do
163
- subject.should_receive(:run).exactly(2).times.and_return(true)
164
- subject.run
165
- sleep(0.1)
166
- subject.reset
167
- end
168
-
169
- it 'starts the demux' do
170
- # add one call to #stop for the :after clause
171
- subject.should_receive(:stop).exactly(2).times.and_return(true)
172
- sleep(0.1)
173
- subject.reset
174
- end
175
- end
176
-
177
- context '#accept' do
178
-
179
- let!(:event){ :echo }
180
- let!(:message){ 'hello world' }
181
-
182
- def setup_demux
183
- @demux = TcpSyncDemux.new(host: 'localhost', port: 5555, acl: %w[allow all])
184
- @demux.run
185
- end
186
-
187
- def send_event_message
188
- there = TCPSocket.open('localhost', 5555)
189
- @thread = Thread.new do
190
- there.puts(@demux.format_message(event, message))
191
- end
192
-
193
- @expected = nil
194
- Timeout::timeout(2) do
195
- @expected = @demux.accept
196
- end
197
- @thread.join(1)
198
- end
199
-
200
- after(:each) do
201
- @demux.stop unless @demux.nil?
202
- Thread.kill(@thread) unless @thread.nil?
203
- @expected = @demux = @thread = nil
204
- end
205
-
206
- it 'returns a correct EventContext object' do
207
- setup_demux
208
- send_event_message
209
- @expected.should be_a(EventContext)
210
- @expected.event.should eq :echo
211
- @expected.args.should eq ['hello world']
212
- @expected.callback.should be_nil
213
- end
214
-
215
- it 'returns nil on exception' do
216
- setup_demux
217
- @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
218
- send_event_message
219
- @expected.should be_nil
220
- end
221
-
222
- it 'returns nil if the ACL rejects the client' do
223
- acl = double('acl')
224
- acl.should_receive(:allow_socket?).with(anything()).and_return(false)
225
- ACL.should_receive(:new).with(anything()).and_return(acl)
226
- setup_demux
227
- send_event_message
228
- @expected.should be_nil
229
- end
230
-
231
- it 'resets the demux on exception' do
232
- setup_demux
233
- @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
234
- @demux.should_receive(:reset).with(no_args())
235
- send_event_message
236
- end
237
- end
238
-
239
- context '#respond' do
240
-
241
- it 'returns nil if the socket is nil' do
242
- subject.stop
243
- subject.respond(:ok, 'foo').should be_nil
244
- end
245
-
246
- it 'puts a message on the socket' do
247
- socket = double('tcp socket')
248
- socket.should_receive(:puts).with("ok\r\necho\r\n\r\n")
249
- subject.instance_variable_set(:@socket, socket)
250
- subject.respond(:ok, 'echo')
251
- end
252
-
253
- it 'resets the demux on exception' do
254
- socket = double('tcp socket')
255
- socket.should_receive(:puts).and_raise(SocketError)
256
- subject.instance_variable_set(:@socket, socket)
257
- subject.should_receive(:reset)
258
- subject.respond(:ok, 'echo')
259
- end
260
- end
261
-
262
- context '#format_message' do
263
-
264
- it 'raises an exception when the event is nil' do
265
- lambda {
266
- subject.format_message(nil)
267
- }.should raise_error(ArgumentError)
268
-
269
- lambda {
270
- TcpSyncDemux.format_message(nil)
271
- }.should raise_error(ArgumentError)
272
- end
273
-
274
- it 'raises an exception when the event is an empty string' do
275
- lambda {
276
- subject.format_message(' ')
277
- }.should raise_error(ArgumentError)
278
-
279
- lambda {
280
- TcpSyncDemux.format_message(' ')
281
- }.should raise_error(ArgumentError)
282
- end
283
-
284
- it 'creates a message with no arguments' do
285
- message = subject.format_message(:echo)
286
- message.should eq "echo\r\n\r\n"
287
-
288
- message = TcpSyncDemux.format_message('echo')
289
- message.should eq "echo\r\n\r\n"
290
- end
291
-
292
- it 'creates a message with arguments' do
293
- message = subject.format_message(:echo, 'hello', 'world')
294
- message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
295
-
296
- message = TcpSyncDemux.format_message('echo', 'hello', 'world')
297
- message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
298
- end
299
- end
300
-
301
- context '#parse_message' do
302
-
303
- it 'accepts the message as a string' do
304
- message = subject.parse_message("echo\r\nhello world\r\n\r\n")
305
- message.should_not eq [nil, nil]
306
- end
307
-
308
- it 'accepts the message as an array of lines' do
309
- message = subject.parse_message(%w[echo hello world])
310
- message.should_not eq [nil, nil]
311
- end
312
-
313
- it 'recognizes an event name beginning with a colon' do
314
- message = subject.parse_message(":echo\r\nhello world\r\n\r\n")
315
- message.first.should eq :echo
316
-
317
- message = TcpSyncDemux.parse_message(%w[:echo hello world])
318
- message.first.should eq :echo
319
- end
320
-
321
- it 'recognizes an event name without a beginning colon' do
322
- message = subject.parse_message(%w[echo hello world])
323
- message.first.should eq :echo
324
-
325
- message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
326
- message.first.should eq :echo
327
- end
328
-
329
- it 'parses a message without arguments' do
330
- message = subject.parse_message("echo\r\n\r\n")
331
- message.first.should eq :echo
332
-
333
- message = TcpSyncDemux.parse_message(%w[echo])
334
- message.first.should eq :echo
335
- end
336
-
337
- it 'parses a message with arguments' do
338
- message = subject.parse_message(%w[echo hello world])
339
- message.last.should eq %w[hello world]
340
-
341
- message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
342
- message.last.should eq ['hello world']
343
- end
344
-
345
- it 'returns nil for a malformed message' do
346
- message = subject.parse_message(nil)
347
- message.should eq [nil, []]
348
-
349
- message = subject.parse_message(' ')
350
- message.should eq [nil, []]
351
- end
352
- end
353
-
354
- specify 'integration', not_on_travis: true do
355
-
356
- # server
357
- demux = Concurrent::Reactor::TcpSyncDemux.new
358
- reactor = Concurrent::Reactor.new(demux)
359
-
360
- reactor.add_handler(:echo) {|*args| args.first }
361
- reactor.add_handler(:error) {|*args| raise StandardError.new(args.first) }
362
- reactor.add_handler(:unknown) {|*args| args.first }
363
- reactor.add_handler(:abend) {|*args| args.first }
364
-
365
- t = Thread.new { reactor.run }
366
- sleep(0.1)
367
-
368
- # client
369
- there = TCPSocket.open(TcpSyncDemux::DEFAULT_HOST, TcpSyncDemux::DEFAULT_PORT)
370
-
371
- # test :ok
372
- 10.times do
373
- message = Faker::Company.bs
374
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:echo, message))
375
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
376
- result.should eq :ok
377
- echo.first.should eq message
378
- end
379
-
380
- # test :ex
381
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:error, 'error'))
382
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
383
- result.should eq :ex
384
- echo.first.should eq 'error'
385
-
386
- # test :noop
387
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:bogus, 'bogus'))
388
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
389
- result.should eq :noop
390
- echo.first.should =~ /bogus/
391
-
392
- # test handler error
393
- ex = ArgumentError.new('abend')
394
- ec = Reactor::EventContext.new(:abend, [])
395
- Reactor::EventContext.should_receive(:new).with(:abend, []).and_return(ec)
396
- reactor.should_receive(:handle_event).with(ec).and_raise(ex)
397
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:abend))
398
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
399
- result.should eq :abend
400
- echo.first.should eq 'abend'
401
-
402
- #cleanup
403
- reactor.stop
404
- sleep(0.1)
405
- Thread.kill(t)
406
- sleep(0.1)
407
- end
408
- end
409
- end
410
- end