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.
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,258 +1,269 @@
1
- require 'spec_helper'
2
-
3
- module Concurrent
4
-
5
- describe Supervisor do
6
-
7
- let(:worker_class) do
8
- Class.new {
9
- behavior(:runnable)
10
- def run() return true; end
11
- def stop() return true; end
12
- def running?() return true; end
13
- }
14
- end
15
-
16
- let(:worker){ worker_class.new }
17
-
18
- subject{ Supervisor.new }
19
-
20
- after(:each) do
21
- subject.stop
22
- end
23
-
24
- context '#initialize' do
25
-
26
- it 'sets the initial length to zero' do
27
- supervisor = Supervisor.new
28
- supervisor.length.should == 0
29
- end
30
-
31
- it 'sets the initial length to one when a worker is provided' do
32
- supervisor = Supervisor.new(worker: worker)
33
- supervisor.length.should == 1
34
- end
35
-
36
- it 'sets the initial state to stopped' do
37
- supervisor = Supervisor.new
38
- supervisor.should_not be_running
39
- end
40
-
41
- it 'sets the monitor interval when given' do
42
- supervisor = Supervisor.new
43
- supervisor.monitor_interval.should == Supervisor::DEFAULT_MONITOR_INTERVAL
44
- end
45
-
46
- it 'sets the monitor interval to the default when not given' do
47
- supervisor = Supervisor.new(monitor_interval: 5)
48
- supervisor.monitor_interval.should == 5
49
-
50
- supervisor = Supervisor.new(monitor: 10)
51
- supervisor.monitor_interval.should == 10
52
- end
53
- end
54
-
55
- context 'run' do
56
-
57
- it 'runs the monitor' do
58
- subject.should_receive(:monitor).with(no_args()).at_least(1).times
59
- t = Thread.new{ subject.run }
60
- sleep(0.1)
61
- subject.stop
62
- Thread.kill(t) unless t.nil?
63
- end
64
-
65
- it 'calls #run on all workers' do
66
- supervisor = Supervisor.new(worker: worker)
67
- # must stub AFTER adding or else #add_worker will reject
68
- worker.should_receive(:run).with(no_args())
69
- t = Thread.new{ supervisor.run }
70
- sleep(0.1)
71
- supervisor.stop
72
- Thread.kill(t)
73
- end
74
-
75
- it 'sets the state to running' do
76
- t = Thread.new{ subject.run }
77
- sleep(0.1)
78
- subject.should be_running
79
- subject.stop
80
- Thread.kill(t)
81
- end
82
-
83
- it 'raises an exception when already running' do
84
- @thread = nil
85
- subject.run!
86
- lambda {
87
- @thread = Thread.new{ subject.run }
88
- @thread.abort_on_exception = true
89
- sleep(0.1)
90
- }.should raise_error(StandardError)
91
- subject.stop
92
- Thread.kill(@thread) unless @thread.nil?
93
- end
94
- end
95
-
96
- context '#run!' do
97
-
98
- it 'runs the monitor thread' do
99
- Thread.should_receive(:new).with(no_args())
100
- subject.run!
101
- end
102
-
103
- it 'calls #run on all workers' do
104
- supervisor = Supervisor.new(worker: worker)
105
- # must stub AFTER adding or else #add_worker will reject
106
- worker.should_receive(:run).with(no_args())
107
- supervisor.run!
108
- sleep(0.1)
109
- end
110
-
111
- it 'sets the state to running' do
112
- subject.run!
113
- subject.should be_running
114
- end
115
-
116
- it 'raises an exception when already running' do
117
- subject.run!
118
- lambda {
119
- subject.run!
120
- }.should raise_error(StandardError)
121
- end
122
- end
123
-
124
- context '#stop' do
125
-
126
- it 'stops the monitor thread' do
127
- Thread.should_receive(:kill).with(anything())
128
- subject.run!
129
- sleep(0.1)
130
- subject.stop
131
- end
132
-
133
- it 'calls #stop on all workers' do
134
- workers = (1..3).collect{ worker_class.new }
135
- workers.each{|worker| subject.add_worker(worker)}
136
- # must stub AFTER adding or else #add_worker will reject
137
- workers.each{|worker| worker.should_receive(:stop).with(no_args())}
138
- subject.run!
139
- sleep(0.1)
140
- subject.stop
141
- end
142
-
143
- it 'sets the state to stopped' do
144
- subject.run!
145
- subject.stop
146
- subject.should_not be_running
147
- end
148
-
149
- it 'returns true immediately when already stopped' do
150
- subject.stop.should be_true
151
- end
152
- end
153
-
154
- context '#running?' do
155
-
156
- it 'returns true when running' do
157
- subject.run!
158
- subject.should be_running
159
- end
160
-
161
- it 'returns false when stopped' do
162
- subject.run!
163
- subject.stop
164
- subject.should_not be_running
165
- end
166
- end
167
-
168
- context '#length' do
169
-
170
- it 'returns a count of attached workers' do
171
- workers = (1..3).collect{ worker.dup }
172
- workers.each{|worker| subject.add_worker(worker)}
173
- subject.length.should == 3
174
- end
175
- end
176
-
177
- context '#add_worker' do
178
-
179
- it 'adds the worker when stopped' do
180
- subject.add_worker(worker)
181
- subject.length.should == 1
182
- end
183
-
184
- it 'rejects the worker when running' do
185
- subject.run!
186
- subject.add_worker(worker)
187
- subject.length.should == 0
188
- end
189
-
190
- it 'rejects a worker without the :runnable behavior' do
191
- subject.add_worker('bogus worker')
192
- subject.length.should == 0
193
- end
194
-
195
- it 'returns true when a worker is accepted' do
196
- subject.add_worker(worker).should be_true
197
- end
198
-
199
- it 'returns false when a worker is not accepted' do
200
- subject.add_worker('bogus worker').should be_false
201
- end
202
- end
203
-
204
- context 'supervision' do
205
-
206
- it 'reruns any worker that stops' do
207
- worker = Class.new(worker_class){
208
- def run() sleep(0.2); end
209
- }.new
210
-
211
- supervisor = Supervisor.new(worker: worker, monitor: 0.1)
212
- supervisor.add_worker(worker)
213
- # must stub AFTER adding or else #add_worker will reject
214
- worker.should_receive(:run).with(no_args()).at_least(2).times
215
- supervisor.run!
216
- sleep(1)
217
- supervisor.stop
218
- end
219
-
220
- it 'reruns any dead threads' do
221
- worker = Class.new(worker_class){
222
- def run() raise StandardError; end
223
- }.new
224
-
225
- supervisor = Supervisor.new(worker: worker, monitor: 0.1)
226
- supervisor.add_worker(worker)
227
- # must stub AFTER adding or else #add_worker will reject
228
- worker.should_receive(:run).with(no_args()).at_least(2).times
229
- supervisor.run!
230
- sleep(1)
231
- supervisor.stop
232
- end
233
- end
234
-
235
- context 'supervisor tree' do
236
-
237
- specify do
238
- s1 = Supervisor.new(monitor: 0.1)
239
- s2 = Supervisor.new(monitor: 0.1)
240
- s3 = Supervisor.new(monitor: 0.1)
241
-
242
- workers = (1..3).collect{ worker_class.new }
243
- workers.each{|worker| s3.add_worker(worker)}
244
- # must stub AFTER adding or else #add_worker will reject
245
- workers.each{|worker| worker.should_receive(:run).with(no_args())}
246
- workers.each{|worker| worker.should_receive(:stop).with(no_args())}
247
-
248
- s1.add_worker(s2)
249
- s2.add_worker(s3)
250
-
251
- s1.run!
252
- sleep(0.1)
253
- s1.stop
254
- sleep(0.1)
255
- end
256
- end
257
- end
258
- end
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Supervisor do
6
+
7
+ let(:worker_class) do
8
+ Class.new {
9
+ behavior(:runnable)
10
+ def run() return true; end
11
+ def stop() return true; end
12
+ def running?() return true; end
13
+ }
14
+ end
15
+
16
+ let(:worker){ worker_class.new }
17
+
18
+ subject{ Supervisor.new }
19
+
20
+ after(:each) do
21
+ subject.stop
22
+ end
23
+
24
+ context '#initialize' do
25
+
26
+ it 'sets the initial length to zero' do
27
+ supervisor = Supervisor.new
28
+ supervisor.length.should == 0
29
+ end
30
+
31
+ it 'sets the initial length to one when a worker is provided' do
32
+ supervisor = Supervisor.new(worker: worker)
33
+ supervisor.length.should == 1
34
+ end
35
+
36
+ it 'sets the initial state to stopped' do
37
+ supervisor = Supervisor.new
38
+ supervisor.should_not be_running
39
+ end
40
+
41
+ it 'sets the monitor interval when given' do
42
+ supervisor = Supervisor.new
43
+ supervisor.monitor_interval.should == Supervisor::DEFAULT_MONITOR_INTERVAL
44
+ end
45
+
46
+ it 'sets the monitor interval to the default when not given' do
47
+ supervisor = Supervisor.new(monitor_interval: 5)
48
+ supervisor.monitor_interval.should == 5
49
+
50
+ supervisor = Supervisor.new(monitor: 10)
51
+ supervisor.monitor_interval.should == 10
52
+ end
53
+ end
54
+
55
+ context 'run' do
56
+
57
+ it 'runs the monitor' do
58
+ subject.should_receive(:monitor).with(no_args()).at_least(1).times
59
+ t = Thread.new{ subject.run }
60
+ sleep(0.1)
61
+ subject.stop
62
+ Thread.kill(t) unless t.nil?
63
+ end
64
+
65
+ it 'calls #run on all workers' do
66
+ supervisor = Supervisor.new(worker: worker)
67
+ # must stub AFTER adding or else #add_worker will reject
68
+ worker.should_receive(:run).with(no_args())
69
+ t = Thread.new{ supervisor.run }
70
+ sleep(0.1)
71
+ supervisor.stop
72
+ Thread.kill(t)
73
+ end
74
+
75
+ it 'sets the state to running' do
76
+ t = Thread.new{ subject.run }
77
+ sleep(0.1)
78
+ subject.should be_running
79
+ subject.stop
80
+ Thread.kill(t)
81
+ end
82
+
83
+ it 'raises an exception when already running' do
84
+ @thread = nil
85
+ subject.run!
86
+ lambda {
87
+ @thread = Thread.new{ subject.run }
88
+ @thread.abort_on_exception = true
89
+ sleep(0.1)
90
+ }.should raise_error(StandardError)
91
+ subject.stop
92
+ Thread.kill(@thread) unless @thread.nil?
93
+ end
94
+ end
95
+
96
+ context '#run!' do
97
+
98
+ it 'runs the monitor thread' do
99
+ thread = Thread.new{ nil }
100
+ Thread.should_receive(:new).with(no_args()).and_return(thread)
101
+ subject.run!
102
+ end
103
+
104
+ it 'calls #run on all workers' do
105
+ supervisor = Supervisor.new(worker: worker)
106
+ # must stub AFTER adding or else #add_worker will reject
107
+ worker.should_receive(:run).with(no_args())
108
+ supervisor.run!
109
+ sleep(0.1)
110
+ end
111
+
112
+ it 'sets the state to running' do
113
+ subject.run!
114
+ subject.should be_running
115
+ end
116
+
117
+ it 'raises an exception when already running' do
118
+ subject.run!
119
+ lambda {
120
+ subject.run!
121
+ }.should raise_error(StandardError)
122
+ end
123
+ end
124
+
125
+ context '#stop' do
126
+
127
+ it 'stops the monitor thread' do
128
+ Thread.should_receive(:kill).with(anything())
129
+ subject.run!
130
+ sleep(0.1)
131
+ subject.stop
132
+ end
133
+
134
+ it 'calls #stop on all workers' do
135
+ workers = (1..3).collect{ worker_class.new }
136
+ workers.each{|worker| subject.add_worker(worker)}
137
+ # must stub AFTER adding or else #add_worker will reject
138
+ workers.each{|worker| worker.should_receive(:stop).with(no_args())}
139
+ subject.run!
140
+ sleep(0.1)
141
+ subject.stop
142
+ end
143
+
144
+ it 'sets the state to stopped' do
145
+ subject.run!
146
+ subject.stop
147
+ subject.should_not be_running
148
+ end
149
+
150
+ it 'returns true immediately when already stopped' do
151
+ subject.stop.should be_true
152
+ end
153
+
154
+ it 'unblocks a thread blocked by #run and exits normally' do
155
+ supervisor = Supervisor.new(monitor: 0.1)
156
+ @thread = Thread.new{ sleep(0.5); supervisor.stop }
157
+ sleep(0.1)
158
+ lambda {
159
+ Timeout::timeout(1){ supervisor.run }
160
+ }.should_not raise_error
161
+ Thread.kill(@thread) unless @thread.nil?
162
+ end
163
+ end
164
+
165
+ context '#running?' do
166
+
167
+ it 'returns true when running' do
168
+ subject.run!
169
+ subject.should be_running
170
+ end
171
+
172
+ it 'returns false when stopped' do
173
+ subject.run!
174
+ subject.stop
175
+ subject.should_not be_running
176
+ end
177
+ end
178
+
179
+ context '#length' do
180
+
181
+ it 'returns a count of attached workers' do
182
+ workers = (1..3).collect{ worker.dup }
183
+ workers.each{|worker| subject.add_worker(worker)}
184
+ subject.length.should == 3
185
+ end
186
+ end
187
+
188
+ context '#add_worker' do
189
+
190
+ it 'adds the worker when stopped' do
191
+ subject.add_worker(worker)
192
+ subject.length.should == 1
193
+ end
194
+
195
+ it 'rejects the worker when running' do
196
+ subject.run!
197
+ subject.add_worker(worker)
198
+ subject.length.should == 0
199
+ end
200
+
201
+ it 'rejects a worker without the :runnable behavior' do
202
+ subject.add_worker('bogus worker')
203
+ subject.length.should == 0
204
+ end
205
+
206
+ it 'returns true when a worker is accepted' do
207
+ subject.add_worker(worker).should be_true
208
+ end
209
+
210
+ it 'returns false when a worker is not accepted' do
211
+ subject.add_worker('bogus worker').should be_false
212
+ end
213
+ end
214
+
215
+ context 'supervision' do
216
+
217
+ it 'reruns any worker that stops' do
218
+ worker = Class.new(worker_class){
219
+ def run() sleep(0.2); end
220
+ }.new
221
+
222
+ supervisor = Supervisor.new(worker: worker, monitor: 0.1)
223
+ supervisor.add_worker(worker)
224
+ # must stub AFTER adding or else #add_worker will reject
225
+ worker.should_receive(:run).with(no_args()).at_least(2).times
226
+ supervisor.run!
227
+ sleep(1)
228
+ supervisor.stop
229
+ end
230
+
231
+ it 'reruns any dead threads' do
232
+ worker = Class.new(worker_class){
233
+ def run() raise StandardError; end
234
+ }.new
235
+
236
+ supervisor = Supervisor.new(worker: worker, monitor: 0.1)
237
+ supervisor.add_worker(worker)
238
+ # must stub AFTER adding or else #add_worker will reject
239
+ worker.should_receive(:run).with(no_args()).at_least(2).times
240
+ supervisor.run!
241
+ sleep(1)
242
+ supervisor.stop
243
+ end
244
+ end
245
+
246
+ context 'supervisor tree' do
247
+
248
+ specify do
249
+ s1 = Supervisor.new(monitor: 0.1)
250
+ s2 = Supervisor.new(monitor: 0.1)
251
+ s3 = Supervisor.new(monitor: 0.1)
252
+
253
+ workers = (1..3).collect{ worker_class.new }
254
+ workers.each{|worker| s3.add_worker(worker)}
255
+ # must stub AFTER adding or else #add_worker will reject
256
+ workers.each{|worker| worker.should_receive(:run).with(no_args())}
257
+ workers.each{|worker| worker.should_receive(:stop).with(no_args())}
258
+
259
+ s1.add_worker(s2)
260
+ s2.add_worker(s3)
261
+
262
+ s1.run!
263
+ sleep(0.1)
264
+ s1.stop
265
+ sleep(0.1)
266
+ end
267
+ end
268
+ end
269
+ end