Skip to content

Commit ce0971d

Browse files
committed
Deprecate timeout
1 parent 6f91c14 commit ce0971d

File tree

4 files changed

+47
-50
lines changed

4 files changed

+47
-50
lines changed

lib/concurrent/agent.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
require 'concurrent/dereferenceable'
44
require 'concurrent/observable'
5-
require 'concurrent/utility/timeout'
65
require 'concurrent/logging'
76

87
module Concurrent
@@ -64,6 +63,7 @@ def rescue(clazz = StandardError, &block)
6463
end
6564
self
6665
end
66+
6767
alias_method :catch, :rescue
6868
alias_method :on_error, :rescue
6969

@@ -87,6 +87,7 @@ def validate(&block)
8787
end
8888
self
8989
end
90+
9091
alias_method :validates, :validate
9192
alias_method :validate_with, :validate
9293
alias_method :validates_with, :validate
@@ -106,20 +107,30 @@ def post(&block)
106107
# Update the current value with the result of the given block fast,
107108
# block can do blocking calls
108109
#
109-
# @param [Fixnum, nil] timeout maximum number of seconds before an update is cancelled
110+
# @param [Fixnum, nil] timeout [DEPRECATED] maximum number of seconds before an update is cancelled
110111
#
111112
# @yield the fast to be performed with the current value in order to calculate
112113
# the new value
113114
# @yieldparam [Object] value the current value
114115
# @yieldreturn [Object] the new value
115116
# @return [true, nil] nil when no block is given
116117
def post_off(timeout = nil, &block)
117-
block = if timeout
118-
lambda { |value| Concurrent::timeout(timeout) { block.call(value) } }
118+
warn '[DEPRECATED] post_off with timeout options is deprecated and will be removed'
119+
task = if timeout
120+
lambda do |value|
121+
future = Future.execute do
122+
block.call(value)
123+
end
124+
if future.wait(timeout)
125+
future.value!
126+
else
127+
raise Concurrent::TimeoutError
128+
end
129+
end
119130
else
120131
block
121132
end
122-
post_on(@io_executor, &block)
133+
post_on(@io_executor, &task)
123134
end
124135

125136
# Update the current value with the result of the given block fast,

lib/concurrent/utility/timeout.rb

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
module Concurrent
77

8-
# Wait the given number of seconds for the block operation to complete.
8+
# [DEPRECATED] Wait the given number of seconds for the block operation to complete.
99
# Intended to be a simpler and more reliable replacement to the Ruby
10-
# standard library `Timeout::timeout` method.
10+
# standard library `Timeout::timeout` method. It does not kill the task
11+
# so it finishes anyway. Advantage is that it cannot cause any ugly errors by
12+
# killing threads.
1113
#
1214
# @param [Integer] seconds The number of seconds to wait
13-
#
1415
# @return [Object] The result of the block operation
1516
#
1617
# @raise [Concurrent::TimeoutError] when the block operation does not complete
@@ -19,20 +20,16 @@ module Concurrent
1920
# @see http://ruby-doc.org/stdlib-2.2.0/libdoc/timeout/rdoc/Timeout.html Ruby Timeout::timeout
2021
#
2122
# @!macro monotonic_clock_warning
22-
def timeout(seconds)
23-
24-
thread = Thread.new do
25-
Thread.current[:result] = yield
26-
end
27-
success = thread.join(seconds)
23+
def timeout(seconds, &block)
24+
warn '[DEPRECATED] timeout is deprecated and will be removed'
2825

29-
if success
30-
return thread[:result]
26+
future = Future.execute(&block)
27+
future.wait(seconds)
28+
if future.complete?
29+
future.value!
3130
else
3231
raise TimeoutError
3332
end
34-
ensure
35-
Thread.kill(thread) unless thread.nil?
3633
end
3734
module_function :timeout
3835
end

spec/concurrent/agent_spec.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,19 @@ module Concurrent
3333
end
3434

3535
it 'times out' do
36-
ex = nil
37-
subject.post_off(0.1) { |v| sleep(0.2); ex = true }
36+
ex = nil
37+
timeout = false
38+
subject.rescue(Concurrent::TimeoutError) { timeout = true }
39+
subject.post_off(0.1) do |v|
40+
sleep(0.2)
41+
ex = true
42+
end
43+
sleep 0.1
3844
subject.await
45+
expect(timeout).to eq false
3946
sleep 0.3
40-
expect(ex).to eq nil
47+
expect(timeout).to eq false
48+
expect(ex).to eq true
4149
end
4250
end
4351

@@ -410,8 +418,10 @@ def trigger_observable(observable)
410418
agent = Agent.new(0, executor: executor)
411419
agent.post { |old| old + continue.value }
412420
sleep 0.1
413-
Concurrent.timeout(0.2) { expect(agent.value).to eq 0 }
421+
expect(agent.value).to eq 0
414422
continue.set 1
423+
agent.await
424+
expect(agent.value).to eq 1
415425
sleep 0.1
416426
end
417427

spec/concurrent/utility/timeout_spec.rb

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,25 @@ module Concurrent
33
describe '#timeout' do
44

55
it 'raises an exception if no block is given' do
6-
expect {
7-
Concurrent::timeout(1)
8-
}.to raise_error
6+
expect { Concurrent::timeout(0.1) }.to raise_error
97
end
108

119
it 'returns the value of the block on success' do
12-
result = Concurrent::timeout(1) { 42 }
10+
result = Concurrent::timeout(0.1) { 42 }
1311
expect(result).to eq 42
1412
end
1513

1614
it 'raises an exception if the timeout value is reached' do
17-
expect {
18-
Concurrent::timeout(1){ sleep }
19-
}.to raise_error(Concurrent::TimeoutError)
15+
expect { Concurrent::timeout(0.1) { sleep 2 } }.to raise_error(Concurrent::TimeoutError)
2016
end
2117

2218
it 'bubbles thread exceptions' do
23-
expect {
24-
Concurrent::timeout(1){ raise NotImplementedError }
25-
}.to raise_error
19+
expect { Concurrent::timeout(0.1) { raise NotImplementedError } }.to raise_error
2620
end
2721

28-
it 'kills the thread on success' do
29-
result = Concurrent::timeout(1) { 42 }
30-
expect(Thread).to receive(:kill).with(any_args())
31-
Concurrent::timeout(1){ 42 }
32-
end
33-
34-
it 'kills the thread on timeout' do
35-
expect(Thread).to receive(:kill).with(any_args())
36-
expect {
37-
Concurrent::timeout(1){ sleep }
38-
}.to raise_error
39-
end
40-
41-
it 'kills the thread on exception' do
42-
expect(Thread).to receive(:kill).with(any_args())
43-
expect {
44-
Concurrent::timeout(1){ raise NotImplementedError }
45-
}.to raise_error
22+
it 'does not kill the thread on timeout' do
23+
expect(Thread).not_to receive(:kill).with(any_args())
24+
expect { Concurrent::timeout(0.1) { sleep 2 } }.to raise_error
4625
end
4726
end
4827
end

0 commit comments

Comments
 (0)