Skip to content

Commit afa281e

Browse files
committed
Replace at_exit hooks with a register
1 parent fb52108 commit afa281e

19 files changed

+204
-238
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ rvm:
77
- 2.0.0
88
- 1.9.3
99
- ruby-head
10-
- jruby-1.7.18
10+
- jruby-1.7.19
1111
- jruby-head
1212
- rbx-2
1313

examples/benchmark_pool.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def initialize(opts = {})
151151
raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
152152

153153
init_executor
154-
enable_at_exit_handler!(opts)
154+
self.auto_terminate = opts.fetch(:auto_terminate, true)
155155

156156
@pool = []
157157
@queue = Queue.new

lib/concurrent/configuration.rb

Lines changed: 46 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,20 @@ module Concurrent
1616
private_constant :GLOBAL_LOGGER
1717

1818
# @!visibility private
19-
AUTO_TERMINATE_GLOBAL_EXECUTORS = AtomicBoolean.new(true)
20-
private_constant :AUTO_TERMINATE_GLOBAL_EXECUTORS
21-
22-
# @!visibility private
23-
AUTO_TERMINATE_ALL_EXECUTORS = AtomicBoolean.new(true)
24-
private_constant :AUTO_TERMINATE_ALL_EXECUTORS
25-
26-
# @!visibility private
27-
GLOBAL_FAST_EXECUTOR = Delay.new do
28-
Concurrent.new_fast_executor(
29-
stop_on_exit: AUTO_TERMINATE_GLOBAL_EXECUTORS.value)
30-
end
19+
GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) }
3120
private_constant :GLOBAL_FAST_EXECUTOR
3221

3322
# @!visibility private
34-
GLOBAL_IO_EXECUTOR = Delay.new do
35-
Concurrent.new_io_executor(
36-
stop_on_exit: AUTO_TERMINATE_GLOBAL_EXECUTORS.value)
37-
end
23+
GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) }
3824
private_constant :GLOBAL_IO_EXECUTOR
3925

4026
# @!visibility private
41-
GLOBAL_TIMER_SET = Delay.new do
42-
TimerSet.new(stop_on_exit: AUTO_TERMINATE_GLOBAL_EXECUTORS.value)
43-
end
27+
GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) }
4428
private_constant :GLOBAL_TIMER_SET
4529

4630
# @!visibility private
4731
GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new
32+
private_constant :GLOBAL_IMMEDIATE_EXECUTOR
4833

4934
def self.global_logger
5035
GLOBAL_LOGGER.value
@@ -54,87 +39,34 @@ def self.global_logger=(value)
5439
GLOBAL_LOGGER.value = value
5540
end
5641

57-
# Defines if global executors should be auto-terminated with an
58-
# `at_exit` callback. When set to `false` it will be the application
59-
# programmer's responsibility to ensure that the global thread pools
60-
# are shutdown properly prior to application exit.
61-
#
62-
# @note Only change this option if you know what you are doing!
63-
# When this is set to true (the default) then `at_exit` handlers
64-
# will be registered automatically for the *global* thread pools
65-
# to ensure that they are shutdown when the application ends. When
66-
# changed to false, the `at_exit` handlers will be circumvented
67-
# for all *global* thread pools. This method should *never* be called
68-
# from within a gem. It should *only* be used from within the main
69-
# application and even then it should be used only when necessary.
70-
#
71-
def self.disable_auto_termination_of_global_executors!
72-
AUTO_TERMINATE_GLOBAL_EXECUTORS.make_false
73-
end
74-
75-
# Defines if global executors should be auto-terminated with an
76-
# `at_exit` callback. When set to `false` it will be the application
77-
# programmer's responsibility to ensure that the global thread pools
78-
# are shutdown properly prior to application exit.
42+
# Disables pool auto-termination which is called on `at_exit` callback.
43+
# When disabled it will be the application
44+
# programmer's responsibility to ensure that the thread pools
45+
# are shutdown properly prior to application exit
46+
# by calling {.terminate_pools!} method.
7947
#
80-
# @note Only change this option if you know what you are doing!
81-
# When this is set to true (the default) then `at_exit` handlers
82-
# will be registered automatically for the *global* thread pools
83-
# to ensure that they are shutdown when the application ends. When
84-
# changed to false, the `at_exit` handlers will be circumvented
85-
# for all *global* thread pools. This method should *never* be called
48+
# @note this option should be needed only because of `at_exit` ordering
49+
# issues which may arise when running some of the testing frameworks.
50+
# E.g. Minitest's test-suite runs itself in `at_exit` callback which
51+
# executes after the pools are already terminated. Then auto termination
52+
# needs to be disabled and called manually after test-suite ends.
53+
# @note This method should *never* be called
8654
# from within a gem. It should *only* be used from within the main
8755
# application and even then it should be used only when necessary.
88-
#
89-
# @return [Boolean] true when global thread pools will auto-terminate on
90-
# application exit using an `at_exit` handler; false when no auto-termination
91-
# will occur.
92-
def self.auto_terminate_global_executors?
93-
AUTO_TERMINATE_GLOBAL_EXECUTORS.value
56+
def self.disable_executor_auto_termination!
57+
Executor::AT_EXIT_AUTO_TERMINATION.enabled = false
9458
end
9559

96-
# Defines if *ALL* executors should be auto-terminated with an
97-
# `at_exit` callback. When set to `false` it will be the application
98-
# programmer's responsibility to ensure that *all* thread pools,
99-
# including the global thread pools, are shutdown properly prior to
100-
# application exit.
101-
#
102-
# @note Only change this option if you know what you are doing!
103-
# When this is set to true (the default) then `at_exit` handlers
104-
# will be registered automatically for *all* thread pools to
105-
# ensure that they are shutdown when the application ends. When
106-
# changed to false, the `at_exit` handlers will be circumvented
107-
# for *all* Concurrent Ruby thread pools running within the
108-
# application. Even those created within other gems used by the
109-
# application. This method should *never* be called from within a
110-
# gem. It should *only* be used from within the main application.
111-
# And even then it should be used only when necessary.
112-
def self.disable_auto_termination_of_all_executors!
113-
AUTO_TERMINATE_ALL_EXECUTORS.make_false
60+
# @return [true,false]
61+
# @see .disable_executor_auto_termination!
62+
def self.disable_executor_auto_termination?
63+
Executor::AT_EXIT_AUTO_TERMINATION.enabled?
11464
end
11565

116-
# Defines if *ALL* executors should be auto-terminated with an
117-
# `at_exit` callback. When set to `false` it will be the application
118-
# programmer's responsibility to ensure that *all* thread pools,
119-
# including the global thread pools, are shutdown properly prior to
120-
# application exit.
121-
#
122-
# @note Only change this option if you know what you are doing!
123-
# When this is set to true (the default) then `at_exit` handlers
124-
# will be registered automatically for *all* thread pools to
125-
# ensure that they are shutdown when the application ends. When
126-
# changed to false, the `at_exit` handlers will be circumvented
127-
# for *all* Concurrent Ruby thread pools running within the
128-
# application. Even those created within other gems used by the
129-
# application. This method should *never* be called from within a
130-
# gem. It should *only* be used from within the main application.
131-
# And even then it should be used only when necessary.
132-
#
133-
# @return [Boolean] true when *all* thread pools will auto-terminate on
134-
# application exit using an `at_exit` handler; false when no auto-termination
135-
# will occur.
136-
def self.auto_terminate_all_executors?
137-
AUTO_TERMINATE_ALL_EXECUTORS.value
66+
# terminates all pools and blocks until they are terminated
67+
# @see .disable_executor_auto_termination!
68+
def self.terminate_pools!
69+
Executor::AT_EXIT_AUTO_TERMINATION.terminate
13870
end
13971

14072
# Global thread pool optimized for short, fast *operations*.
@@ -151,6 +83,10 @@ def self.global_io_executor
15183
GLOBAL_IO_EXECUTOR.value
15284
end
15385

86+
def self.global_immediate_executor
87+
GLOBAL_IMMEDIATE_EXECUTOR
88+
end
89+
15490
# Global thread pool user for global *timers*.
15591
#
15692
# @return [Concurrent::TimerSet] the thread pool
@@ -160,44 +96,25 @@ def self.global_timer_set
16096
GLOBAL_TIMER_SET.value
16197
end
16298

163-
def self.shutdown_global_executors
164-
global_fast_executor.shutdown
165-
global_io_executor.shutdown
166-
global_timer_set.shutdown
167-
end
168-
169-
def self.kill_global_executors
170-
global_fast_executor.kill
171-
global_io_executor.kill
172-
global_timer_set.kill
173-
end
174-
175-
def self.wait_for_global_executors_termination(timeout = nil)
176-
latch = CountDownLatch.new(3)
177-
[ global_fast_executor, global_io_executor, global_timer_set ].each do |executor|
178-
Thread.new{ executor.wait_for_termination(timeout); latch.count_down }
179-
end
180-
latch.wait(timeout)
181-
end
182-
18399
def self.new_fast_executor(opts = {})
184100
FixedThreadPool.new(
185-
[2, Concurrent.processor_count].max,
186-
stop_on_exit: opts.fetch(:stop_on_exit, true),
187-
idletime: 60, # 1 minute
188-
max_queue: 0, # unlimited
189-
fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
101+
[2, Concurrent.processor_count].max,
102+
auto_terminate: opts.fetch(:auto_terminate, true),
103+
idletime: 60, # 1 minute
104+
max_queue: 0, # unlimited
105+
fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
190106
)
191107
end
192108

193109
def self.new_io_executor(opts = {})
194110
ThreadPoolExecutor.new(
195-
min_threads: [2, Concurrent.processor_count].max,
196-
max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
197-
stop_on_exit: opts.fetch(:stop_on_exit, true),
198-
idletime: 60, # 1 minute
199-
max_queue: 0, # unlimited
200-
fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
111+
min_threads: [2, Concurrent.processor_count].max,
112+
max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
113+
# max_threads: 1000,
114+
auto_terminate: opts.fetch(:auto_terminate, true),
115+
idletime: 60, # 1 minute
116+
max_queue: 0, # unlimited
117+
fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
201118
)
202119
end
203120

@@ -256,15 +173,15 @@ def global_timer_set
256173
def global_task_pool=(executor)
257174
warn '[DEPRECATED] Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
258175
GLOBAL_IO_EXECUTOR.reconfigure { executor } or
259-
raise ConfigurationError.new('global task pool was already set')
176+
raise ConfigurationError.new('global task pool was already set')
260177
end
261178

262179
# @deprecated Replacing global thread pools is deprecated.
263180
# Use the :executor constructor option instead.
264181
def global_operation_pool=(executor)
265182
warn '[DEPRECATED] Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
266183
GLOBAL_FAST_EXECUTOR.reconfigure { executor } or
267-
raise ConfigurationError.new('global operation pool was already set')
184+
raise ConfigurationError.new('global operation pool was already set')
268185
end
269186

270187
# @deprecated Use Concurrent.new_io_executor instead
@@ -293,11 +210,12 @@ def auto_terminate
293210
end
294211

295212
# create the default configuration on load
296-
@configuration = Atomic.new(Configuration.new)
213+
CONFIGURATION = Atomic.new(Configuration.new)
214+
private_constant :CONFIGURATION
297215

298216
# @return [Configuration]
299217
def self.configuration
300-
@configuration.value
218+
CONFIGURATION.value
301219
end
302220

303221
# Perform gem-level configuration.

0 commit comments

Comments
 (0)