concurrent-ruby 0.1.1 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +48 -1
- data/lib/concurrent.rb +8 -1
- data/lib/concurrent/agent.rb +19 -40
- data/lib/concurrent/cached_thread_pool.rb +10 -11
- data/lib/concurrent/defer.rb +8 -12
- data/lib/concurrent/executor.rb +95 -0
- data/lib/concurrent/fixed_thread_pool.rb +12 -6
- data/lib/concurrent/functions.rb +120 -0
- data/lib/concurrent/future.rb +8 -20
- data/lib/concurrent/global_thread_pool.rb +13 -0
- data/lib/concurrent/goroutine.rb +5 -1
- data/lib/concurrent/null_thread_pool.rb +22 -0
- data/lib/concurrent/obligation.rb +10 -64
- data/lib/concurrent/promise.rb +38 -60
- data/lib/concurrent/reactor.rb +166 -0
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -0
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -0
- data/lib/concurrent/supervisor.rb +100 -0
- data/lib/concurrent/thread_pool.rb +16 -5
- data/lib/concurrent/utilities.rb +8 -0
- data/lib/concurrent/version.rb +1 -1
- data/md/defer.md +4 -4
- data/md/executor.md +187 -0
- data/md/promise.md +2 -0
- data/md/thread_pool.md +27 -0
- data/spec/concurrent/agent_spec.rb +8 -27
- data/spec/concurrent/cached_thread_pool_spec.rb +14 -1
- data/spec/concurrent/defer_spec.rb +17 -21
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +159 -149
- data/spec/concurrent/executor_spec.rb +200 -0
- data/spec/concurrent/fixed_thread_pool_spec.rb +2 -3
- data/spec/concurrent/functions_spec.rb +217 -0
- data/spec/concurrent/future_spec.rb +4 -11
- data/spec/concurrent/global_thread_pool_spec.rb +38 -0
- data/spec/concurrent/goroutine_spec.rb +15 -0
- data/spec/concurrent/null_thread_pool_spec.rb +54 -0
- data/spec/concurrent/obligation_shared.rb +127 -116
- data/spec/concurrent/promise_spec.rb +16 -14
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -0
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -0
- data/spec/concurrent/reactor_spec.rb +364 -0
- data/spec/concurrent/supervisor_spec.rb +258 -0
- data/spec/concurrent/thread_pool_shared.rb +156 -161
- data/spec/concurrent/utilities_spec.rb +30 -1
- data/spec/spec_helper.rb +13 -0
- metadata +38 -9
@@ -2,241 +2,251 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
require 'concurrent/agent'
|
4
4
|
require 'concurrent/future'
|
5
|
+
require 'concurrent/goroutine'
|
5
6
|
require 'concurrent/promise'
|
6
7
|
|
7
|
-
|
8
|
+
if Functional::PLATFORM.mri?
|
8
9
|
|
9
|
-
|
10
|
+
module Concurrent
|
10
11
|
|
11
|
-
|
12
|
+
describe EventMachineDeferProxy do
|
12
13
|
|
13
|
-
|
14
|
+
subject { EventMachineDeferProxy.new }
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
EventMachine.run do
|
18
|
-
subject.post{ @expected = true }
|
19
|
-
sleep(0.1)
|
20
|
-
EventMachine.stop
|
21
|
-
end
|
22
|
-
@expected.should eq true
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'proxies a call with arguments' do
|
26
|
-
@expected = []
|
27
|
-
EventMachine.run do
|
28
|
-
subject.post(1,2,3){|*args| @expected = args }
|
29
|
-
sleep(0.1)
|
30
|
-
EventMachine.stop
|
31
|
-
end
|
32
|
-
@expected.should eq [1,2,3]
|
16
|
+
after(:all) do
|
17
|
+
$GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
|
33
18
|
end
|
34
19
|
|
35
|
-
|
36
|
-
@expected = false
|
37
|
-
EventMachine.run do
|
38
|
-
subject << proc{ @expected = true }
|
39
|
-
sleep(0.1)
|
40
|
-
EventMachine.stop
|
41
|
-
end
|
42
|
-
@expected.should eq true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'operation' do
|
47
|
-
|
48
|
-
context 'goroutine' do
|
49
|
-
|
50
|
-
it 'passes all arguments to the block' do
|
51
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
20
|
+
context '#post' do
|
52
21
|
|
22
|
+
it 'proxies a call without arguments' do
|
23
|
+
@expected = false
|
53
24
|
EventMachine.run do
|
54
|
-
|
55
|
-
@expected = nil
|
56
|
-
go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
|
25
|
+
subject.post{ @expected = true }
|
57
26
|
sleep(0.1)
|
58
|
-
@expected.should eq [3, 2, 1]
|
59
|
-
|
60
27
|
EventMachine.stop
|
61
28
|
end
|
29
|
+
@expected.should eq true
|
62
30
|
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context Agent do
|
66
|
-
|
67
|
-
subject { Agent.new(0) }
|
68
|
-
|
69
|
-
it 'supports fulfillment' do
|
70
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
71
31
|
|
32
|
+
it 'proxies a call with arguments' do
|
33
|
+
@expected = []
|
72
34
|
EventMachine.run do
|
73
|
-
|
74
|
-
@expected = []
|
75
|
-
subject.post{ @expected << 1 }
|
76
|
-
subject.post{ @expected << 2 }
|
77
|
-
subject.post{ @expected << 3 }
|
35
|
+
subject.post(1,2,3){|*args| @expected = args }
|
78
36
|
sleep(0.1)
|
79
|
-
@expected.should eq [1,2,3]
|
80
|
-
|
81
37
|
EventMachine.stop
|
82
38
|
end
|
39
|
+
@expected.should eq [1,2,3]
|
83
40
|
end
|
84
41
|
|
85
|
-
it '
|
86
|
-
|
87
|
-
|
42
|
+
it 'aliases #<<' do
|
43
|
+
@expected = false
|
88
44
|
EventMachine.run do
|
89
|
-
|
90
|
-
@expected = nil
|
91
|
-
subject.validate{ @expected = 10; true }
|
92
|
-
subject.post{ nil }
|
45
|
+
subject << proc{ @expected = true }
|
93
46
|
sleep(0.1)
|
94
|
-
@expected.should eq 10
|
95
|
-
|
96
47
|
EventMachine.stop
|
97
48
|
end
|
49
|
+
@expected.should eq true
|
98
50
|
end
|
51
|
+
end
|
99
52
|
|
100
|
-
|
101
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
102
|
-
|
103
|
-
EventMachine.run do
|
53
|
+
context 'operation' do
|
104
54
|
|
105
|
-
|
106
|
-
subject.
|
107
|
-
on_error(StandardError){|ex| @expected = 1 }.
|
108
|
-
on_error(StandardError){|ex| @expected = 2 }.
|
109
|
-
on_error(StandardError){|ex| @expected = 3 }
|
110
|
-
subject.post{ raise StandardError }
|
111
|
-
sleep(0.1)
|
112
|
-
@expected.should eq 1
|
55
|
+
context 'goroutine' do
|
113
56
|
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context Future do
|
57
|
+
it 'passes all arguments to the block' do
|
58
|
+
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
120
59
|
|
121
|
-
|
122
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
60
|
+
EventMachine.run do
|
123
61
|
|
124
|
-
|
62
|
+
@expected = nil
|
63
|
+
go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
|
64
|
+
sleep(0.1)
|
65
|
+
@expected.should eq [3, 2, 1]
|
125
66
|
|
126
|
-
|
127
|
-
f = Future.new(1, 2, 3) do |a, b, c|
|
128
|
-
@a, @b, @c = a, b, c
|
67
|
+
EventMachine.stop
|
129
68
|
end
|
130
|
-
sleep(0.1)
|
131
|
-
[@a, @b, @c].should eq [1, 2, 3]
|
132
|
-
|
133
|
-
sleep(0.1)
|
134
|
-
EventMachine.stop
|
135
69
|
end
|
136
70
|
end
|
137
|
-
end
|
138
71
|
|
139
|
-
|
72
|
+
context Agent do
|
140
73
|
|
141
|
-
|
74
|
+
subject { Agent.new(0) }
|
142
75
|
|
143
|
-
|
144
|
-
|
76
|
+
before(:each) do
|
77
|
+
Agent.thread_pool = EventMachineDeferProxy.new
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'supports fulfillment' do
|
145
81
|
|
146
82
|
EventMachine.run do
|
147
83
|
|
148
|
-
@
|
149
|
-
|
150
|
-
|
151
|
-
|
84
|
+
@expected = []
|
85
|
+
subject.post{ @expected << 1 }
|
86
|
+
subject.post{ @expected << 2 }
|
87
|
+
subject.post{ @expected << 3 }
|
152
88
|
sleep(0.1)
|
153
|
-
|
89
|
+
@expected.should eq [1,2,3]
|
154
90
|
|
155
|
-
sleep(0.1)
|
156
91
|
EventMachine.stop
|
157
92
|
end
|
158
93
|
end
|
159
94
|
|
160
|
-
it '
|
161
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
95
|
+
it 'supports validation' do
|
162
96
|
|
163
97
|
EventMachine.run do
|
98
|
+
|
164
99
|
@expected = nil
|
165
|
-
|
100
|
+
subject.validate{ @expected = 10; true }
|
101
|
+
subject.post{ nil }
|
166
102
|
sleep(0.1)
|
167
|
-
@expected.should eq
|
103
|
+
@expected.should eq 10
|
168
104
|
|
169
|
-
sleep(0.1)
|
170
105
|
EventMachine.stop
|
171
106
|
end
|
172
107
|
end
|
173
108
|
|
174
|
-
it '
|
175
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
109
|
+
it 'supports rejection' do
|
176
110
|
|
177
111
|
EventMachine.run do
|
178
112
|
|
179
|
-
|
113
|
+
@expected = nil
|
114
|
+
subject.
|
115
|
+
on_error(StandardError){|ex| @expected = 1 }.
|
116
|
+
on_error(StandardError){|ex| @expected = 2 }.
|
117
|
+
on_error(StandardError){|ex| @expected = 3 }
|
118
|
+
subject.post{ raise StandardError }
|
180
119
|
sleep(0.1)
|
181
|
-
|
120
|
+
@expected.should eq 1
|
182
121
|
|
183
|
-
sleep(0.1)
|
184
122
|
EventMachine.stop
|
185
123
|
end
|
186
124
|
end
|
187
125
|
end
|
188
126
|
|
189
|
-
context
|
127
|
+
context Future do
|
190
128
|
|
191
|
-
|
192
|
-
|
129
|
+
before(:each) do
|
130
|
+
Future.thread_pool = EventMachineDeferProxy.new
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'supports fulfillment' do
|
193
134
|
|
194
135
|
EventMachine.run do
|
195
136
|
|
196
|
-
|
137
|
+
@a = @b = @c = nil
|
138
|
+
f = Future.new(1, 2, 3) do |a, b, c|
|
139
|
+
@a, @b, @c = a, b, c
|
140
|
+
end
|
197
141
|
sleep(0.1)
|
198
|
-
|
199
|
-
p.reason.should.to_s =~ /Boom!/
|
200
|
-
p.should be_rejected
|
142
|
+
[@a, @b, @c].should eq [1, 2, 3]
|
201
143
|
|
202
144
|
sleep(0.1)
|
203
145
|
EventMachine.stop
|
204
146
|
end
|
205
147
|
end
|
148
|
+
end
|
206
149
|
|
207
|
-
|
208
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
150
|
+
context Promise do
|
209
151
|
|
210
|
-
|
152
|
+
before(:each) do
|
153
|
+
Promise.thread_pool = EventMachineDeferProxy.new
|
154
|
+
end
|
211
155
|
|
212
|
-
|
213
|
-
Promise.new{ raise StandardError }.
|
214
|
-
on_error(StandardError){|ex| @expected = 1 }.
|
215
|
-
on_error(StandardError){|ex| @expected = 2 }.
|
216
|
-
on_error(StandardError){|ex| @expected = 3 }
|
217
|
-
sleep(0.1)
|
218
|
-
@expected.should eq 1
|
156
|
+
context 'fulfillment' do
|
219
157
|
|
220
|
-
|
221
|
-
|
158
|
+
it 'passes all arguments to the first promise in the chain' do
|
159
|
+
|
160
|
+
EventMachine.run do
|
161
|
+
|
162
|
+
@a = @b = @c = nil
|
163
|
+
p = Promise.new(1, 2, 3) do |a, b, c|
|
164
|
+
@a, @b, @c = a, b, c
|
165
|
+
end
|
166
|
+
sleep(0.1)
|
167
|
+
[@a, @b, @c].should eq [1, 2, 3]
|
168
|
+
|
169
|
+
sleep(0.1)
|
170
|
+
EventMachine.stop
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'passes the result of each block to all its children' do
|
175
|
+
|
176
|
+
EventMachine.run do
|
177
|
+
@expected = nil
|
178
|
+
Promise.new(10){|a| a * 2 }.then{|result| @expected = result}
|
179
|
+
sleep(0.1)
|
180
|
+
@expected.should eq 20
|
181
|
+
|
182
|
+
sleep(0.1)
|
183
|
+
EventMachine.stop
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'sets the promise value to the result if its block' do
|
188
|
+
|
189
|
+
EventMachine.run do
|
190
|
+
|
191
|
+
p = Promise.new(10){|a| a * 2 }.then{|result| result * 2}
|
192
|
+
sleep(0.1)
|
193
|
+
p.value.should eq 40
|
194
|
+
|
195
|
+
sleep(0.1)
|
196
|
+
EventMachine.stop
|
197
|
+
end
|
222
198
|
end
|
223
199
|
end
|
224
200
|
|
225
|
-
|
226
|
-
$GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
|
201
|
+
context 'rejection' do
|
227
202
|
|
228
|
-
|
203
|
+
it 'sets the promise reason and error on exception' do
|
229
204
|
|
230
|
-
|
231
|
-
Promise.new{ raise StandardError }.
|
232
|
-
on_error(ArgumentError){|ex| @expected = ex }.
|
233
|
-
on_error(LoadError){|ex| @expected = ex }.
|
234
|
-
on_error(Exception){|ex| @expected = ex }
|
235
|
-
sleep(0.1)
|
236
|
-
@expected.should be_a(StandardError)
|
205
|
+
EventMachine.run do
|
237
206
|
|
238
|
-
|
239
|
-
|
207
|
+
p = Promise.new{ raise StandardError.new('Boom!') }
|
208
|
+
sleep(0.1)
|
209
|
+
p.reason.should be_a(Exception)
|
210
|
+
p.reason.should.to_s =~ /Boom!/
|
211
|
+
p.should be_rejected
|
212
|
+
|
213
|
+
sleep(0.1)
|
214
|
+
EventMachine.stop
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'calls the first exception block with a matching class' do
|
219
|
+
|
220
|
+
EventMachine.run do
|
221
|
+
|
222
|
+
@expected = nil
|
223
|
+
Promise.new{ raise StandardError }.
|
224
|
+
on_error(StandardError){|ex| @expected = 1 }.
|
225
|
+
on_error(StandardError){|ex| @expected = 2 }.
|
226
|
+
on_error(StandardError){|ex| @expected = 3 }
|
227
|
+
sleep(0.1)
|
228
|
+
@expected.should eq 1
|
229
|
+
|
230
|
+
sleep(0.1)
|
231
|
+
EventMachine.stop
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'passes the exception object to the matched block' do
|
236
|
+
|
237
|
+
EventMachine.run do
|
238
|
+
|
239
|
+
@expected = nil
|
240
|
+
Promise.new{ raise StandardError }.
|
241
|
+
on_error(ArgumentError){|ex| @expected = ex }.
|
242
|
+
on_error(LoadError){|ex| @expected = ex }.
|
243
|
+
on_error(Exception){|ex| @expected = ex }
|
244
|
+
sleep(0.1)
|
245
|
+
@expected.should be_a(StandardError)
|
246
|
+
|
247
|
+
sleep(0.1)
|
248
|
+
EventMachine.stop
|
249
|
+
end
|
240
250
|
end
|
241
251
|
end
|
242
252
|
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Executor do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@orig_stdout = $stdout
|
9
|
+
$stdout = StringIO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
$stdout = @orig_stdout
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
@ec.kill unless @ec.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#run' do
|
21
|
+
|
22
|
+
it 'raises an exception if no block given' do
|
23
|
+
lambda {
|
24
|
+
@ec = Concurrent::Executor.run('Foo')
|
25
|
+
}.should raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'uses the default execution interval when no interval is given' do
|
29
|
+
@ec = Executor.run('Foo'){ nil }
|
30
|
+
@ec.execution_interval.should eq Executor::EXECUTION_INTERVAL
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'uses the default timeout interval when no interval is given' do
|
34
|
+
@ec = Executor.run('Foo'){ nil }
|
35
|
+
@ec.timeout_interval.should eq Executor::TIMEOUT_INTERVAL
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'uses the given execution interval' do
|
39
|
+
@ec = Executor.run('Foo', execution_interval: 5){ nil }
|
40
|
+
@ec.execution_interval.should eq 5
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'uses the given timeout interval' do
|
44
|
+
@ec = Executor.run('Foo', timeout_interval: 5){ nil }
|
45
|
+
@ec.timeout_interval.should eq 5
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'creates a new thread' do
|
49
|
+
thread = Thread.new{ sleep(1) }
|
50
|
+
Thread.should_receive(:new).with(any_args()).and_return(thread)
|
51
|
+
@ec = Executor.run('Foo'){ nil }
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns an ExecutionContext' do
|
55
|
+
@ec = Executor.run('Foo'){ nil }
|
56
|
+
@ec.should be_a(Executor::ExecutionContext)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'sets the #name context variable' do
|
60
|
+
@ec = Executor.run('Foo'){ nil }
|
61
|
+
@ec.name.should eq 'Foo'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'execution' do
|
66
|
+
|
67
|
+
it 'runs the block immediately when the :run_now option is true' do
|
68
|
+
@expected = false
|
69
|
+
@ec = Executor.run('Foo', execution: 500, now: true){ @expected = true }
|
70
|
+
@expected.should be_false
|
71
|
+
sleep(1)
|
72
|
+
@expected.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'waits for :execution_interval seconds when the :run_now option is false' do
|
76
|
+
@expected = false
|
77
|
+
@ec = Executor.run('Foo', execution: 0.5, now: false){ @expected = true }
|
78
|
+
@expected.should be_false
|
79
|
+
sleep(1)
|
80
|
+
@expected.should be_true
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'waits for :execution_interval seconds when the :run_now option is not given' do
|
84
|
+
@expected = false
|
85
|
+
@ec = Executor.run('Foo', execution: 0.5){ @expected = true }
|
86
|
+
@expected.should be_false
|
87
|
+
sleep(1)
|
88
|
+
@expected.should be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'yields to the execution block' do
|
92
|
+
@expected = false
|
93
|
+
@ec = Executor.run('Foo', execution: 1){ @expected = true }
|
94
|
+
sleep(2)
|
95
|
+
@expected.should be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'passes any given arguments to the execution block' do
|
99
|
+
args = [1,2,3,4]
|
100
|
+
@expected = nil
|
101
|
+
@ec = Executor.run('Foo', execution_interval: 0.5, args: args) do |*args|
|
102
|
+
@expected = args
|
103
|
+
end
|
104
|
+
sleep(1)
|
105
|
+
@expected.should eq args
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'supresses exceptions thrown by the execution block' do
|
109
|
+
lambda {
|
110
|
+
@ec = Executor.run('Foo', execution_interval: 0.5) { raise StandardError }
|
111
|
+
sleep(1)
|
112
|
+
}.should_not raise_error
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'kills the worker thread if the timeout is reached' do
|
116
|
+
# the after(:each) block will trigger this expectation
|
117
|
+
Thread.should_receive(:kill).at_least(1).with(any_args())
|
118
|
+
@ec = Executor.run('Foo', execution_interval: 0.5, timeout_interval: 0.5){ Thread.stop }
|
119
|
+
sleep(1.5)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'logging' do
|
124
|
+
|
125
|
+
before(:each) do
|
126
|
+
@name = nil
|
127
|
+
@level = nil
|
128
|
+
@msg = nil
|
129
|
+
|
130
|
+
@logger = proc do |name, level, msg|
|
131
|
+
@name = name
|
132
|
+
@level = level
|
133
|
+
@msg = msg
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'uses a custom logger when given' do
|
138
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
|
139
|
+
sleep(0.5)
|
140
|
+
@name.should eq 'Foo'
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'logs :info when execution is successful' do
|
144
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
|
145
|
+
sleep(0.5)
|
146
|
+
@level.should eq :info
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'logs :warn when execution times out' do
|
150
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, timeout_interval: 0.1, logger: @logger){ Thread.stop }
|
151
|
+
sleep(0.5)
|
152
|
+
@level.should eq :warn
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'logs :error when execution is fails' do
|
156
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ raise StandardError }
|
157
|
+
sleep(0.5)
|
158
|
+
@level.should eq :error
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context '#status' do
|
163
|
+
|
164
|
+
it 'returns the status of the executor thread when running' do
|
165
|
+
@ec = Executor.run('Foo'){ nil }
|
166
|
+
sleep(0.1)
|
167
|
+
@ec.status.should eq 'sleep'
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'returns nil when not running' do
|
171
|
+
@ec = Executor.run('Foo'){ nil }
|
172
|
+
@ec.kill
|
173
|
+
sleep(0.1)
|
174
|
+
@ec.status.should be_nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context '#join' do
|
179
|
+
|
180
|
+
it 'joins the executor thread when running' do
|
181
|
+
@ec = Executor.run('Foo'){ nil }
|
182
|
+
Thread.new{ sleep(1); @ec.kill }
|
183
|
+
@ec.join.should be_a(Thread)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'joins the executor thread with timeout when running' do
|
187
|
+
@ec = Executor.run('Foo'){ nil }
|
188
|
+
@ec.join(1).should be_nil
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'immediately returns nil when not running' do
|
192
|
+
@ec = Executor.run('Foo'){ nil }
|
193
|
+
@ec.kill
|
194
|
+
sleep(0.1)
|
195
|
+
@ec.join.should be_nil
|
196
|
+
@ec.join(1).should be_nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|