concurrent-ruby 0.1.0 → 0.1.1.pre.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 +279 -224
- data/lib/concurrent.rb +27 -20
- data/lib/concurrent/agent.rb +106 -130
- data/lib/concurrent/cached_thread_pool.rb +130 -122
- data/lib/concurrent/defer.rb +67 -69
- data/lib/concurrent/drb_async_demux.rb +72 -0
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +87 -0
- data/lib/concurrent/fixed_thread_pool.rb +89 -89
- data/lib/concurrent/functions.rb +120 -0
- data/lib/concurrent/future.rb +52 -42
- data/lib/concurrent/global_thread_pool.rb +3 -3
- data/lib/concurrent/goroutine.rb +29 -25
- data/lib/concurrent/obligation.rb +67 -121
- data/lib/concurrent/promise.rb +172 -194
- data/lib/concurrent/reactor.rb +162 -0
- data/lib/concurrent/smart_mutex.rb +66 -0
- data/lib/concurrent/tcp_sync_demux.rb +96 -0
- data/lib/concurrent/thread_pool.rb +65 -61
- data/lib/concurrent/utilities.rb +34 -0
- 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 +176 -0
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +225 -225
- data/md/thread_pool.md +197 -197
- data/spec/concurrent/agent_spec.rb +376 -405
- data/spec/concurrent/cached_thread_pool_spec.rb +112 -112
- data/spec/concurrent/defer_spec.rb +209 -199
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +250 -246
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +146 -0
- data/spec/concurrent/fixed_thread_pool_spec.rb +84 -84
- data/spec/concurrent/functions_spec.rb +57 -0
- data/spec/concurrent/future_spec.rb +125 -115
- data/spec/concurrent/goroutine_spec.rb +67 -52
- data/spec/concurrent/obligation_shared.rb +121 -121
- data/spec/concurrent/promise_spec.rb +299 -310
- data/spec/concurrent/smart_mutex_spec.rb +234 -0
- data/spec/concurrent/thread_pool_shared.rb +209 -209
- data/spec/concurrent/utilities_spec.rb +74 -0
- data/spec/spec_helper.rb +21 -19
- metadata +38 -14
- checksums.yaml +0 -7
@@ -1,134 +1,134 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
describe Event do
|
6
|
-
|
7
|
-
subject{ Event.new }
|
8
|
-
|
9
|
-
context '#initialize' do
|
10
|
-
|
11
|
-
it 'sets the state to unset' do
|
12
|
-
subject.should_not be_set
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
context '#set?' do
|
17
|
-
|
18
|
-
it 'returns true when the event has been set' do
|
19
|
-
subject.set
|
20
|
-
subject.should be_set
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'returns false if the event is unset' do
|
24
|
-
subject.reset
|
25
|
-
subject.should_not be_set
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context '#set' do
|
30
|
-
|
31
|
-
it 'triggers the event' do
|
32
|
-
subject.reset
|
33
|
-
@expected = false
|
34
|
-
Thread.new{ subject.wait; @expected = true }
|
35
|
-
sleep(0.1)
|
36
|
-
subject.set
|
37
|
-
sleep(0.1)
|
38
|
-
@expected.should be_true
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sets the state to set' do
|
42
|
-
subject.set
|
43
|
-
subject.should be_set
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context '#reset' do
|
48
|
-
|
49
|
-
it 'sets the state to unset' do
|
50
|
-
subject.set
|
51
|
-
subject.should be_set
|
52
|
-
subject.reset
|
53
|
-
subject.should_not be_set
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context '#pulse' do
|
58
|
-
|
59
|
-
it 'triggers the event' do
|
60
|
-
subject.reset
|
61
|
-
@expected = false
|
62
|
-
Thread.new{ subject.wait; @expected = true }
|
63
|
-
sleep(0.1)
|
64
|
-
subject.pulse
|
65
|
-
sleep(0.1)
|
66
|
-
@expected.should be_true
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'sets the state to unset' do
|
70
|
-
subject.pulse
|
71
|
-
sleep(0.1)
|
72
|
-
subject.should_not be_set
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
context '#wait' do
|
77
|
-
|
78
|
-
it 'returns immediately when the event has been set' do
|
79
|
-
subject.reset
|
80
|
-
@expected = false
|
81
|
-
subject.set
|
82
|
-
Thread.new{ subject.wait(1000); @expected = true}
|
83
|
-
sleep(1)
|
84
|
-
@expected.should be_true
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'returns true once the event is set' do
|
88
|
-
subject.set
|
89
|
-
subject.wait.should be_true
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'blocks indefinitely when the timer is nil' do
|
93
|
-
subject.reset
|
94
|
-
@expected = false
|
95
|
-
Thread.new{ subject.wait; @expected = true}
|
96
|
-
subject.set
|
97
|
-
sleep(1)
|
98
|
-
@expected.should be_true
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'stops waiting when the timer expires' do
|
102
|
-
subject.reset
|
103
|
-
@expected = false
|
104
|
-
Thread.new{ subject.wait(0.5); @expected = true}
|
105
|
-
sleep(1)
|
106
|
-
@expected.should be_true
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'returns false when the timer expires' do
|
110
|
-
subject.reset
|
111
|
-
subject.wait(1).should be_false
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'triggers multiple waiting threads' do
|
115
|
-
subject.reset
|
116
|
-
@expected = []
|
117
|
-
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
118
|
-
subject.set
|
119
|
-
sleep(1)
|
120
|
-
@expected.length.should eq 5
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'behaves appropriately if wait begins while #set is processing' do
|
124
|
-
subject.reset
|
125
|
-
@expected = []
|
126
|
-
5.times{ Thread.new{ subject.wait(5) } }
|
127
|
-
subject.set
|
128
|
-
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
129
|
-
sleep(1)
|
130
|
-
@expected.length.should eq 5
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Event do
|
6
|
+
|
7
|
+
subject{ Event.new }
|
8
|
+
|
9
|
+
context '#initialize' do
|
10
|
+
|
11
|
+
it 'sets the state to unset' do
|
12
|
+
subject.should_not be_set
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context '#set?' do
|
17
|
+
|
18
|
+
it 'returns true when the event has been set' do
|
19
|
+
subject.set
|
20
|
+
subject.should be_set
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns false if the event is unset' do
|
24
|
+
subject.reset
|
25
|
+
subject.should_not be_set
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#set' do
|
30
|
+
|
31
|
+
it 'triggers the event' do
|
32
|
+
subject.reset
|
33
|
+
@expected = false
|
34
|
+
Thread.new{ subject.wait; @expected = true }
|
35
|
+
sleep(0.1)
|
36
|
+
subject.set
|
37
|
+
sleep(0.1)
|
38
|
+
@expected.should be_true
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'sets the state to set' do
|
42
|
+
subject.set
|
43
|
+
subject.should be_set
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context '#reset' do
|
48
|
+
|
49
|
+
it 'sets the state to unset' do
|
50
|
+
subject.set
|
51
|
+
subject.should be_set
|
52
|
+
subject.reset
|
53
|
+
subject.should_not be_set
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#pulse' do
|
58
|
+
|
59
|
+
it 'triggers the event' do
|
60
|
+
subject.reset
|
61
|
+
@expected = false
|
62
|
+
Thread.new{ subject.wait; @expected = true }
|
63
|
+
sleep(0.1)
|
64
|
+
subject.pulse
|
65
|
+
sleep(0.1)
|
66
|
+
@expected.should be_true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'sets the state to unset' do
|
70
|
+
subject.pulse
|
71
|
+
sleep(0.1)
|
72
|
+
subject.should_not be_set
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '#wait' do
|
77
|
+
|
78
|
+
it 'returns immediately when the event has been set' do
|
79
|
+
subject.reset
|
80
|
+
@expected = false
|
81
|
+
subject.set
|
82
|
+
Thread.new{ subject.wait(1000); @expected = true}
|
83
|
+
sleep(1)
|
84
|
+
@expected.should be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns true once the event is set' do
|
88
|
+
subject.set
|
89
|
+
subject.wait.should be_true
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'blocks indefinitely when the timer is nil' do
|
93
|
+
subject.reset
|
94
|
+
@expected = false
|
95
|
+
Thread.new{ subject.wait; @expected = true}
|
96
|
+
subject.set
|
97
|
+
sleep(1)
|
98
|
+
@expected.should be_true
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'stops waiting when the timer expires' do
|
102
|
+
subject.reset
|
103
|
+
@expected = false
|
104
|
+
Thread.new{ subject.wait(0.5); @expected = true}
|
105
|
+
sleep(1)
|
106
|
+
@expected.should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'returns false when the timer expires' do
|
110
|
+
subject.reset
|
111
|
+
subject.wait(1).should be_false
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'triggers multiple waiting threads' do
|
115
|
+
subject.reset
|
116
|
+
@expected = []
|
117
|
+
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
118
|
+
subject.set
|
119
|
+
sleep(1)
|
120
|
+
@expected.length.should eq 5
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'behaves appropriately if wait begins while #set is processing' do
|
124
|
+
subject.reset
|
125
|
+
@expected = []
|
126
|
+
5.times{ Thread.new{ subject.wait(5) } }
|
127
|
+
subject.set
|
128
|
+
5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
|
129
|
+
sleep(1)
|
130
|
+
@expected.length.should eq 5
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,146 @@
|
|
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 'waits for #execution_interval seconds before executing the block' do
|
68
|
+
@expected = false
|
69
|
+
@ec = Executor.run('Foo', execution: 0.5){ @expected = true }
|
70
|
+
@expected.should be_false
|
71
|
+
sleep(1)
|
72
|
+
@expected.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'yields to the execution block' do
|
76
|
+
@expected = false
|
77
|
+
@ec = Executor.run('Foo', execution: 1){ @expected = true }
|
78
|
+
sleep(2)
|
79
|
+
@expected.should be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'passes any given arguments to the execution block' do
|
83
|
+
args = [1,2,3,4]
|
84
|
+
@expected = nil
|
85
|
+
@ec = Executor.run('Foo', execution_interval: 0.5, args: args) do |*args|
|
86
|
+
@expected = args
|
87
|
+
end
|
88
|
+
sleep(1)
|
89
|
+
@expected.should eq args
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'supresses exceptions thrown by the execution block' do
|
93
|
+
lambda {
|
94
|
+
@ec = Executor.run('Foo', execution_interval: 0.5) { raise StandardError }
|
95
|
+
sleep(1)
|
96
|
+
}.should_not raise_error
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'kills the worker thread if the timeout is reached' do
|
100
|
+
# the after(:each) block will trigger this expectation
|
101
|
+
Thread.should_receive(:kill).at_least(1).with(any_args())
|
102
|
+
@ec = Executor.run('Foo', execution_interval: 0.5, timeout_interval: 0.5){ Thread.stop }
|
103
|
+
sleep(1.5)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'logging' do
|
108
|
+
|
109
|
+
before(:each) do
|
110
|
+
@name = nil
|
111
|
+
@level = nil
|
112
|
+
@msg = nil
|
113
|
+
|
114
|
+
@logger = proc do |name, level, msg|
|
115
|
+
@name = name
|
116
|
+
@level = level
|
117
|
+
@msg = msg
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'uses a custom logger when given' do
|
122
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
|
123
|
+
sleep(0.5)
|
124
|
+
@name.should eq 'Foo'
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'logs :info when execution is successful' do
|
128
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ nil }
|
129
|
+
sleep(0.5)
|
130
|
+
@level.should eq :info
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'logs :warn when execution times out' do
|
134
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, timeout_interval: 0.1, logger: @logger){ Thread.stop }
|
135
|
+
sleep(0.5)
|
136
|
+
@level.should eq :warn
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'logs :error when execution is fails' do
|
140
|
+
@ec = Executor.run('Foo', execution_interval: 0.1, logger: @logger){ raise StandardError }
|
141
|
+
sleep(0.5)
|
142
|
+
@level.should eq :error
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -1,84 +1,84 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative 'thread_pool_shared'
|
3
|
-
|
4
|
-
module Concurrent
|
5
|
-
|
6
|
-
describe FixedThreadPool do
|
7
|
-
|
8
|
-
subject { FixedThreadPool.new(5) }
|
9
|
-
|
10
|
-
it_should_behave_like 'Thread Pool'
|
11
|
-
|
12
|
-
context '#initialize' do
|
13
|
-
|
14
|
-
it 'raises an exception when the pool size is less than one' do
|
15
|
-
lambda {
|
16
|
-
FixedThreadPool.new(0)
|
17
|
-
}.should raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'raises an exception when the pool size is greater than 1024' do
|
21
|
-
lambda {
|
22
|
-
FixedThreadPool.new(1025)
|
23
|
-
}.should raise_error(ArgumentError)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'creates a thread pool of the given size' do
|
27
|
-
thread = double('thread')
|
28
|
-
# add one for the garbage collector
|
29
|
-
Thread.should_receive(:new).exactly(5+1).times.and_return(thread)
|
30
|
-
pool = FixedThreadPool.new(5)
|
31
|
-
pool.size.should eq 5
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'aliases Concurrent#new_fixed_thread_pool' do
|
35
|
-
pool = Concurrent.new_fixed_thread_pool(5)
|
36
|
-
pool.should be_a(FixedThreadPool)
|
37
|
-
pool.size.should eq 5
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context '#kill' do
|
42
|
-
|
43
|
-
it 'kills all threads' do
|
44
|
-
Thread.should_receive(:kill).
|
45
|
-
pool = FixedThreadPool.new(5)
|
46
|
-
pool.kill
|
47
|
-
sleep(0.1)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
context '#size' do
|
52
|
-
|
53
|
-
let(:pool_size) { 3 }
|
54
|
-
subject { FixedThreadPool.new(pool_size) }
|
55
|
-
|
56
|
-
it 'returns the size of the subject when running' do
|
57
|
-
subject.size.should eq pool_size
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'returns zero while shutting down' do
|
61
|
-
subject.post{ sleep(1) }
|
62
|
-
subject.shutdown
|
63
|
-
subject.size.should eq 0
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'returns zero once shut down' do
|
67
|
-
subject.shutdown
|
68
|
-
subject.size.should eq 0
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context 'exception handling' do
|
73
|
-
|
74
|
-
it 'restarts threads that experience exception' do
|
75
|
-
pool = FixedThreadPool.new(5)
|
76
|
-
3.times{ pool << proc{ raise StandardError } }
|
77
|
-
sleep(2)
|
78
|
-
pool.size.should eq 5
|
79
|
-
pool.status.should_not include(nil)
|
80
|
-
#pool.status.include?(nil).should be_false
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'thread_pool_shared'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
describe FixedThreadPool do
|
7
|
+
|
8
|
+
subject { FixedThreadPool.new(5) }
|
9
|
+
|
10
|
+
it_should_behave_like 'Thread Pool'
|
11
|
+
|
12
|
+
context '#initialize' do
|
13
|
+
|
14
|
+
it 'raises an exception when the pool size is less than one' do
|
15
|
+
lambda {
|
16
|
+
FixedThreadPool.new(0)
|
17
|
+
}.should raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises an exception when the pool size is greater than 1024' do
|
21
|
+
lambda {
|
22
|
+
FixedThreadPool.new(1025)
|
23
|
+
}.should raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'creates a thread pool of the given size' do
|
27
|
+
thread = double('thread')
|
28
|
+
# add one for the garbage collector
|
29
|
+
Thread.should_receive(:new).exactly(5+1).times.and_return(thread)
|
30
|
+
pool = FixedThreadPool.new(5)
|
31
|
+
pool.size.should eq 5
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'aliases Concurrent#new_fixed_thread_pool' do
|
35
|
+
pool = Concurrent.new_fixed_thread_pool(5)
|
36
|
+
pool.should be_a(FixedThreadPool)
|
37
|
+
pool.size.should eq 5
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#kill' do
|
42
|
+
|
43
|
+
it 'kills all threads' do
|
44
|
+
Thread.should_receive(:kill).at_least(5).times
|
45
|
+
pool = FixedThreadPool.new(5)
|
46
|
+
pool.kill
|
47
|
+
sleep(0.1)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context '#size' do
|
52
|
+
|
53
|
+
let(:pool_size) { 3 }
|
54
|
+
subject { FixedThreadPool.new(pool_size) }
|
55
|
+
|
56
|
+
it 'returns the size of the subject when running' do
|
57
|
+
subject.size.should eq pool_size
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns zero while shutting down' do
|
61
|
+
subject.post{ sleep(1) }
|
62
|
+
subject.shutdown
|
63
|
+
subject.size.should eq 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns zero once shut down' do
|
67
|
+
subject.shutdown
|
68
|
+
subject.size.should eq 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'exception handling' do
|
73
|
+
|
74
|
+
it 'restarts threads that experience exception' do
|
75
|
+
pool = FixedThreadPool.new(5)
|
76
|
+
3.times{ pool << proc{ raise StandardError } }
|
77
|
+
sleep(2)
|
78
|
+
pool.size.should eq 5
|
79
|
+
pool.status.should_not include(nil)
|
80
|
+
#pool.status.include?(nil).should be_false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|