Skip to content

Commit 96d579a

Browse files
committed
Wrote specs for gem configuration.
1 parent ccc6390 commit 96d579a

File tree

4 files changed

+83
-86
lines changed

4 files changed

+83
-86
lines changed

lib/concurrent/configuration.rb

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,58 @@
1+
require 'thread'
12
require 'concurrent/thread_pool_executor'
23
require 'concurrent/processor_count'
34

45
module Concurrent
6+
7+
ConfigurationError = Class.new(StandardError)
8+
59
class << self
610
attr_accessor :configuration
711
end
812

913
def self.configure
10-
yield(configuration)
14+
(@mutex ||= Mutex.new).synchronize do
15+
yield(configuration)
16+
end
1117
end
1218

1319
class Configuration
1420
attr_accessor :global_task_pool
1521
attr_accessor :global_operation_pool
1622

1723
def initialize
18-
end
19-
20-
def cores
2124
@cores ||= Concurrent::processor_count
2225
end
2326

2427
def global_task_pool
2528
@global_task_pool ||= Concurrent::ThreadPoolExecutor.new(
26-
min_threads: [2, cores].max,
27-
max_threads: [20, cores * 15].max,
28-
idletime: 2 * 60, # 2 minutes
29-
max_queue: 0, # unlimited
30-
overflow_policy: :abort # raise an exception
29+
min_threads: [2, @cores].max,
30+
max_threads: [20, @cores * 15].max,
31+
idletime: 2 * 60, # 2 minutes
32+
max_queue: 0, # unlimited
33+
overflow_policy: :abort # raise an exception
3134
)
3235
end
3336

3437
def global_operation_pool
35-
@global_operation_pool = Concurrent::ThreadPoolExecutor.new(
36-
min_threads: [2, cores].max,
37-
max_threads: [2, cores].max,
38-
idletime: 10 * 60, # 10 minutes
39-
max_queue: [20, cores * 15].max,
40-
overflow_policy: :abort # raise an exception
38+
@global_operation_pool ||= Concurrent::ThreadPoolExecutor.new(
39+
min_threads: [2, @cores].max,
40+
max_threads: [2, @cores].max,
41+
idletime: 10 * 60, # 10 minutes
42+
max_queue: [20, @cores * 15].max,
43+
overflow_policy: :abort # raise an exception
4144
)
4245
end
4346

4447
def global_task_pool=(executor)
45-
finalize_executor(@global_task_pool)
48+
raise ConfigurationError.new('global task pool was already set') unless @global_task_pool.nil?
4649
@global_task_pool = executor
4750
end
4851

4952
def global_operation_pool=(executor)
50-
finalize_executor(@global_operation_pool)
53+
raise ConfigurationError.new('global operation pool was already set') unless @global_operation_pool.nil?
5154
@global_operation_pool = executor
5255
end
53-
54-
private
55-
56-
def finalize_executor(executor)
57-
return if executor.nil?
58-
if executor.respond_to?(:shutdown)
59-
executor.shutdown
60-
elsif executor.respond_to?(:kill)
61-
executor.kill
62-
end
63-
rescue
64-
# suppress
65-
end
6656
end
6757

6858
module OptionsParser
@@ -78,22 +68,25 @@ def get_executor_from(opts = {})
7868
end
7969
end
8070

81-
def task(*args, &block)
82-
Concurrent.configuration.global_task_pool.post(*args, &block)
83-
end
84-
module_function :task
71+
private
8572

86-
def operation(*args, &block)
87-
Concurrent.configuration.global_operation_pool.post(*args, &block)
73+
def self.finalize_executor(executor)
74+
return if executor.nil?
75+
if executor.respond_to?(:shutdown)
76+
executor.shutdown
77+
elsif executor.respond_to?(:kill)
78+
executor.kill
79+
end
80+
rescue
81+
# suppress
8882
end
89-
module_function :operation
9083

9184
# create the default configuration on load
9285
self.configuration = Configuration.new
9386

9487
# set exit hook to shutdown global thread pools
9588
at_exit do
96-
self.configuration.global_task_pool = nil
97-
self.configuration.global_operation_pool = nil
89+
self.finalize_executor(self.configuration.global_task_pool)
90+
self.finalize_executor(self.configuration.global_operation_pool)
9891
end
9992
end

spec/concurrent/configuration_spec.rb

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,6 @@
22

33
module Concurrent
44

5-
describe 'module functions' do
6-
7-
let(:executor){ ImmediateExecutor.new }
8-
9-
specify '#task posts to the global task pool' do
10-
Concurrent.configuration.should_receive(:global_task_pool).and_return(executor)
11-
executor.should_receive(:post).with(1, 2, 3)
12-
Concurrent::task(1, 2, 3){|a, b, c| nil }
13-
end
14-
15-
specify '#operation posts to the global operation pool' do
16-
Concurrent.configuration.should_receive(:global_operation_pool).and_return(executor)
17-
executor.should_receive(:post).with(1, 2, 3)
18-
Concurrent::operation(1, 2, 3){|a, b, c| nil }
19-
end
20-
end
21-
225
describe OptionsParser do
236

247
subject do
@@ -92,39 +75,60 @@ module Concurrent
9275

9376
describe Configuration do
9477

95-
context 'configure' do
96-
97-
it 'raises an exception if called twice'
98-
99-
it 'raises an exception if called after tasks post to the thread pool'
100-
101-
it 'raises an exception if called after operations post to the thread pool'
78+
context 'global task pool' do
10279

103-
it 'allows reconfiguration if set to :test mode'
104-
end
80+
specify 'reader creates a default pool when first called if none exists' do
81+
Concurrent.configuration.global_task_pool.should_not be_nil
82+
Concurrent.configuration.global_task_pool.should respond_to(:post)
83+
end
10584

106-
context '#global_task_pool' do
107-
pending
108-
end
85+
specify 'writer memoizes the given executor' do
86+
executor = ImmediateExecutor.new
87+
Concurrent.configure do |config|
88+
config.global_task_pool = executor
89+
end
90+
Concurrent.configuration.global_task_pool.should eq executor
91+
end
10992

110-
context '#global_task_pool=' do
111-
pending
93+
specify 'writer raises an exception if called twice' do
94+
executor = ImmediateExecutor.new
95+
Concurrent.configure do |config|
96+
config.global_task_pool = executor
97+
end
98+
expect {
99+
Concurrent.configure do |config|
100+
config.global_task_pool = executor
101+
end
102+
}.to raise_error(ConfigurationError)
103+
end
112104
end
113105

114-
context '#global_operation_pool' do
115-
pending
116-
end
106+
context 'global operation pool' do
117107

118-
context '#global_operation_pool=' do
119-
pending
120-
end
108+
specify 'reader creates a default pool when first called if none exists' do
109+
Concurrent.configuration.global_operation_pool.should_not be_nil
110+
Concurrent.configuration.global_operation_pool.should respond_to(:post)
111+
end
121112

122-
context 'cores' do
123-
pending
124-
end
113+
specify 'writer memoizes the given executor' do
114+
executor = ImmediateExecutor.new
115+
Concurrent.configure do |config|
116+
config.global_operation_pool = executor
117+
end
118+
Concurrent.configuration.global_operation_pool.should eq executor
119+
end
125120

126-
context '#at_exit shutdown hook' do
127-
pending
121+
specify 'writer raises an exception if called twice' do
122+
executor = ImmediateExecutor.new
123+
Concurrent.configure do |config|
124+
config.global_operation_pool = executor
125+
end
126+
expect {
127+
Concurrent.configure do |config|
128+
config.global_operation_pool = executor
129+
end
130+
}.to raise_error(ConfigurationError)
131+
end
128132
end
129133
end
130134
end

spec/spec_helper.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,13 @@
2727
end
2828

2929
config.before(:each) do
30+
reset_gem_configuration
3031
end
3132

3233
config.after(:each) do
3334
Thread.list.each do |thread|
3435
thread.kill unless thread == Thread.current
3536
end
36-
37-
Concurrent.configure do |config|
38-
config.global_task_pool = nil
39-
config.global_operation_pool = nil
40-
end
4137
end
4238

4339
config.after(:suite) do

spec/support/functions.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ def jruby?
1919
def rbx?
2020
RbConfig::CONFIG['ruby_install_name']=~ /^rbx$/i
2121
end
22+
23+
def reset_gem_configuration
24+
Concurrent.instance_variable_set(:@configuration, Concurrent::Configuration.new)
25+
end

0 commit comments

Comments
 (0)