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,364 +0,0 @@
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,74 +0,0 @@
1
- require 'spec_helper'
2
- require 'thread'
3
-
4
- describe 'utilities' do
5
-
6
- context '#atomic' do
7
-
8
- it 'calls the block' do
9
- @expected = false
10
- atomic{ @expected = true }
11
- @expected.should be_true
12
- end
13
-
14
- it 'passes all arguments to the block' do
15
- @expected = nil
16
- atomic(1, 2, 3, 4) do |a, b, c, d|
17
- @expected = [a, b, c, d]
18
- end
19
- @expected.should eq [1, 2, 3, 4]
20
- end
21
-
22
- it 'returns the result of the block' do
23
- expected = atomic{ 'foo' }
24
- expected.should eq 'foo'
25
- end
26
-
27
- it 'raises an exception if no block is given' do
28
- lambda {
29
- atomic()
30
- }.should raise_error(ArgumentError)
31
- end
32
-
33
- it 'creates a new Fiber' do
34
- fiber = Fiber.new{ 'foo' }
35
- Fiber.should_receive(:new).with(no_args()).and_return(fiber)
36
- atomic{ 'foo' }
37
- end
38
-
39
- it 'immediately runs the Fiber' do
40
- fiber = Fiber.new{ 'foo' }
41
- Fiber.stub(:new).with(no_args()).and_return(fiber)
42
- fiber.should_receive(:resume).with(no_args())
43
- atomic{ 'foo' }
44
- end
45
- end
46
-
47
- context Mutex do
48
-
49
- context '#sync_with_timeout' do
50
-
51
- it 'returns the result of the block if a lock is obtained before timeout' do
52
- mutex = Mutex.new
53
- result = mutex.sync_with_timeout(30){ 42 }
54
- result.should eq 42
55
- end
56
-
57
- it 'raises Timeout::Error if the timeout is exceeded' do
58
- mutex = Mutex.new
59
- thread = Thread.new{ mutex.synchronize{ sleep(30) } }
60
- sleep(0.1)
61
- lambda {
62
- mutex.sync_and_wait(1)
63
- }.should raise_error(NoMethodError)
64
- Thread.kill(thread)
65
- end
66
-
67
- it 'raises an exception if no block given' do
68
- lambda {
69
- Mutex.new.sync_with_timeout()
70
- }.should raise_error(ArgumentError)
71
- end
72
- end
73
- end
74
- end