concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2
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 +16 -0
- data/lib/concurrent.rb +9 -29
- data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
- data/lib/concurrent/actor/actor_context.rb +77 -0
- data/lib/concurrent/actor/actor_ref.rb +67 -0
- data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
- data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
- data/lib/concurrent/actors.rb +5 -0
- data/lib/concurrent/agent.rb +81 -47
- data/lib/concurrent/async.rb +35 -35
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
- data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
- data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
- data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +103 -0
- data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
- data/lib/concurrent/atomics.rb +9 -0
- data/lib/concurrent/channel/buffered_channel.rb +6 -4
- data/lib/concurrent/channel/channel.rb +30 -2
- data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
- data/lib/concurrent/channel/waitable_list.rb +3 -1
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +68 -19
- data/lib/concurrent/dataflow.rb +9 -9
- data/lib/concurrent/delay.rb +21 -13
- data/lib/concurrent/dereferenceable.rb +40 -33
- data/lib/concurrent/exchanger.rb +3 -0
- data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
- data/lib/concurrent/executor/executor.rb +222 -0
- data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
- data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
- data/lib/concurrent/executor/one_by_one.rb +65 -0
- data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
- data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
- data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +138 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +39 -40
- data/lib/concurrent/ivar.rb +22 -15
- data/lib/concurrent/mvar.rb +2 -1
- data/lib/concurrent/obligation.rb +9 -3
- data/lib/concurrent/observable.rb +33 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +23 -24
- data/lib/concurrent/scheduled_task.rb +21 -45
- data/lib/concurrent/timer_task.rb +204 -126
- data/lib/concurrent/tvar.rb +1 -1
- data/lib/concurrent/utilities.rb +3 -36
- data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
- data/lib/concurrent/utility/timeout.rb +36 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
- data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
- data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
- data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
- data/spec/concurrent/agent_spec.rb +160 -71
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
- data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
- data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
- data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
- data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
- data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
- data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
- data/spec/concurrent/channel/channel_spec.rb +6 -4
- data/spec/concurrent/channel/probe_spec.rb +37 -9
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
- data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
- data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
- data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
- data/spec/concurrent/configuration_spec.rb +4 -70
- data/spec/concurrent/dereferenceable_shared.rb +5 -4
- data/spec/concurrent/exchanger_spec.rb +10 -5
- data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
- data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
- data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
- data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
- data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
- data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
- data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
- data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
- data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
- data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
- data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
- data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
- data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
- data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
- data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
- data/spec/concurrent/executor/timer_set_spec.rb +183 -0
- data/spec/concurrent/future_spec.rb +12 -0
- data/spec/concurrent/ivar_spec.rb +11 -1
- data/spec/concurrent/observable_shared.rb +173 -0
- data/spec/concurrent/observable_spec.rb +51 -0
- data/spec/concurrent/options_parser_spec.rb +71 -0
- data/spec/concurrent/runnable_shared.rb +6 -0
- data/spec/concurrent/scheduled_task_spec.rb +60 -40
- data/spec/concurrent/timer_task_spec.rb +130 -144
- data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
- data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
- data/spec/concurrent/utility/timer_spec.rb +52 -0
- metadata +147 -108
- data/lib/concurrent/actor_context.rb +0 -31
- data/lib/concurrent/actor_ref.rb +0 -39
- data/lib/concurrent/atomic.rb +0 -121
- data/lib/concurrent/channel/probe.rb +0 -19
- data/lib/concurrent/count_down_latch.rb +0 -60
- data/lib/concurrent/event.rb +0 -80
- data/lib/concurrent/java_cached_thread_pool.rb +0 -45
- data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
- data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
- data/lib/concurrent/simple_actor_ref.rb +0 -124
- data/lib/concurrent/thread_pool_executor.rb +0 -30
- data/spec/concurrent/atomic_spec.rb +0 -201
- data/spec/concurrent/count_down_latch_spec.rb +0 -125
- data/spec/concurrent/safe_task_executor_spec.rb +0 -58
- data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
data/lib/concurrent/ivar.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
3
|
require 'concurrent/obligation'
|
4
|
-
require 'concurrent/
|
4
|
+
require 'concurrent/observable'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
@@ -9,20 +9,21 @@ module Concurrent
|
|
9
9
|
|
10
10
|
class IVar
|
11
11
|
include Obligation
|
12
|
+
include Concurrent::Observable
|
12
13
|
|
13
14
|
NO_VALUE = Object.new
|
14
15
|
|
15
|
-
# Create a new
|
16
|
+
# Create a new `Ivar` in the `:pending` state with the (optional) initial value.
|
16
17
|
#
|
17
18
|
# @param [Object] value the initial value
|
18
19
|
# @param [Hash] opts the options to create a message with
|
19
|
-
# @option opts [String] :dup_on_deref (false) call
|
20
|
-
# @option opts [String] :freeze_on_deref (false) call
|
21
|
-
# @option opts [String] :copy_on_deref (nil) call the given
|
20
|
+
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
21
|
+
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
22
|
+
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
22
23
|
# returning the value returned from the proc
|
23
24
|
def initialize(value = NO_VALUE, opts = {})
|
24
25
|
init_obligation
|
25
|
-
|
26
|
+
self.observers = CopyOnWriteObserverSet.new
|
26
27
|
set_deref_options(opts)
|
27
28
|
|
28
29
|
if value == NO_VALUE
|
@@ -34,26 +35,32 @@ module Concurrent
|
|
34
35
|
|
35
36
|
# Add an observer on this object that will receive notification on update.
|
36
37
|
#
|
37
|
-
# Upon completion the
|
38
|
-
# method of the observer will be called with three arguments: the
|
39
|
-
#
|
40
|
-
# and the final
|
38
|
+
# Upon completion the `IVar` will notify all observers in a thread-say way. The `func`
|
39
|
+
# method of the observer will be called with three arguments: the `Time` at which the
|
40
|
+
# `Future` completed the asynchronous operation, the final `value` (or `nil` on rejection),
|
41
|
+
# and the final `reason` (or `nil` on fulfillment).
|
41
42
|
#
|
42
43
|
# @param [Object] observer the object that will be notified of changes
|
43
|
-
# @param [Symbol] func symbol naming the method to call when this
|
44
|
-
def add_observer(observer, func = :update)
|
44
|
+
# @param [Symbol] func symbol naming the method to call when this `Observable` has changes`
|
45
|
+
def add_observer(observer = nil, func = :update, &block)
|
46
|
+
raise ArgumentError.new('cannot provide both an observer and a block') if observer && block
|
45
47
|
direct_notification = false
|
46
48
|
|
49
|
+
if block
|
50
|
+
observer = block
|
51
|
+
func = :call
|
52
|
+
end
|
53
|
+
|
47
54
|
mutex.synchronize do
|
48
55
|
if event.set?
|
49
56
|
direct_notification = true
|
50
57
|
else
|
51
|
-
|
58
|
+
observers.add_observer(observer, func)
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
55
62
|
observer.send(func, Time.now, self.value, reason) if direct_notification
|
56
|
-
|
63
|
+
observer
|
57
64
|
end
|
58
65
|
|
59
66
|
def set(value)
|
@@ -72,7 +79,7 @@ module Concurrent
|
|
72
79
|
end
|
73
80
|
|
74
81
|
time = Time.now
|
75
|
-
|
82
|
+
observers.notify_and_delete_observers{ [time, self.value, reason] }
|
76
83
|
self
|
77
84
|
end
|
78
85
|
end
|
data/lib/concurrent/mvar.rb
CHANGED
@@ -2,7 +2,7 @@ require 'thread'
|
|
2
2
|
require 'timeout'
|
3
3
|
|
4
4
|
require 'concurrent/dereferenceable'
|
5
|
-
require 'concurrent/event'
|
5
|
+
require 'concurrent/atomic/event'
|
6
6
|
|
7
7
|
module Concurrent
|
8
8
|
|
@@ -48,11 +48,17 @@ module Concurrent
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def state
|
51
|
-
mutex.
|
51
|
+
mutex.lock
|
52
|
+
result = @state
|
53
|
+
mutex.unlock
|
54
|
+
result
|
52
55
|
end
|
53
56
|
|
54
57
|
def reason
|
55
|
-
mutex.
|
58
|
+
mutex.lock
|
59
|
+
result = @reason
|
60
|
+
mutex.unlock
|
61
|
+
result
|
56
62
|
end
|
57
63
|
|
58
64
|
protected
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'concurrent/atomic/copy_on_notify_observer_set'
|
2
|
+
require 'concurrent/atomic/copy_on_write_observer_set'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
module Observable
|
7
|
+
|
8
|
+
# @return [Object] the added observer
|
9
|
+
def add_observer(*args, &block)
|
10
|
+
observers.add_observer(*args, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Object] the deleted observer
|
14
|
+
def delete_observer(*args)
|
15
|
+
observers.delete_observer(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Observable] self
|
19
|
+
def delete_observers
|
20
|
+
observers.delete_observers
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] the observers count
|
25
|
+
def count_observers
|
26
|
+
observers.count_observers
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_accessor :observers
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# A mixin module for parsing options hashes related to gem-level configuration.
|
4
|
+
module OptionsParser
|
5
|
+
|
6
|
+
# Get the requested `Executor` based on the values set in the options hash.
|
7
|
+
#
|
8
|
+
# @param [Hash] opts the options defining the requested executor
|
9
|
+
# @option opts [Executor] :executor (`nil`) when set use the given `Executor` instance
|
10
|
+
# @option opts [Boolean] :operation (`false`) when true use the global operation pool
|
11
|
+
# @option opts [Boolean] :task (`true`) when true use the global task pool
|
12
|
+
#
|
13
|
+
# @return [Executor] the requested thread pool (default: global task pool)
|
14
|
+
def get_executor_from(opts = {})
|
15
|
+
if opts[:executor]
|
16
|
+
opts[:executor]
|
17
|
+
elsif opts[:operation] == true || opts[:task] == false
|
18
|
+
Concurrent.configuration.global_operation_pool
|
19
|
+
else
|
20
|
+
Concurrent.configuration.global_task_pool
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the requested `Executor` based on the values set in the options hash.
|
25
|
+
#
|
26
|
+
# @param [Hash] opts the options defining the requested executor
|
27
|
+
# @option opts [Executor] :task_executor (`nil`) when set use the given `Executor` instance
|
28
|
+
#
|
29
|
+
# @return [Executor] the requested thread pool (default: global task pool)
|
30
|
+
def get_task_executor_from(opts = {})
|
31
|
+
opts[:task_executor] || opts[:executor] || Concurrent.configuration.global_task_pool
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get the requested `Executor` based on the values set in the options hash.
|
35
|
+
#
|
36
|
+
# @param [Hash] opts the options defining the requested executor
|
37
|
+
# @option opts [Executor] :task_executor (`nil`) when set use the given `Executor` instance
|
38
|
+
#
|
39
|
+
# @return [Executor] the requested thread pool (default: global operation pool)
|
40
|
+
def get_operation_executor_from(opts = {})
|
41
|
+
opts[:operation_executor] || opts[:executor] || Concurrent.configuration.global_operation_pool
|
42
|
+
end
|
43
|
+
|
44
|
+
extend self
|
45
|
+
end
|
46
|
+
end
|
data/lib/concurrent/promise.rb
CHANGED
@@ -1,45 +1,43 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
-
require 'concurrent/configuration'
|
4
3
|
require 'concurrent/obligation'
|
4
|
+
require 'concurrent/options_parser'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
8
|
class Promise
|
9
9
|
include Obligation
|
10
|
-
include OptionsParser
|
11
10
|
|
12
11
|
# Initialize a new Promise with the provided options.
|
13
12
|
#
|
14
|
-
# @param [Object] initial the initial value
|
15
13
|
# @param [Hash] opts the options used to define the behavior at update and deref
|
16
14
|
#
|
17
|
-
# @option opts [Promise] :parent the parent
|
15
|
+
# @option opts [Promise] :parent the parent `Promise` when building a chain/tree
|
18
16
|
# @option opts [Proc] :on_fulfill fulfillment handler
|
19
17
|
# @option opts [Proc] :on_reject rejection handler
|
20
18
|
#
|
21
|
-
# @option opts [Boolean] :operation (false) when
|
22
|
-
# operation pool (for long-running operations), when
|
19
|
+
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
|
20
|
+
# operation pool (for long-running operations), when `false` will execute the future on the
|
23
21
|
# global task pool (for short-running tasks)
|
24
22
|
# @option opts [object] :executor when provided will run all operations on
|
25
23
|
# this executor rather than the global thread pool (overrides :operation)
|
26
24
|
#
|
27
|
-
# @option opts [String] :dup_on_deref (false) call
|
28
|
-
# @option opts [String] :freeze_on_deref (false) call
|
29
|
-
# @option opts [String] :copy_on_deref (nil) call the given
|
25
|
+
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
26
|
+
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
27
|
+
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
30
28
|
# returning the value returned from the proc
|
31
29
|
#
|
32
30
|
# @see http://wiki.commonjs.org/wiki/Promises/A
|
33
31
|
# @see http://promises-aplus.github.io/promises-spec/
|
34
32
|
def initialize(opts = {}, &block)
|
35
|
-
opts.delete_if {|k, v| v.nil?}
|
33
|
+
opts.delete_if { |k, v| v.nil? }
|
36
34
|
|
37
|
-
@executor = get_executor_from(opts)
|
35
|
+
@executor = OptionsParser::get_executor_from(opts)
|
38
36
|
@parent = opts.fetch(:parent) { nil }
|
39
|
-
@on_fulfill = opts.fetch(:on_fulfill) { Proc.new{ |result| result } }
|
40
|
-
@on_reject = opts.fetch(:on_reject) { Proc.new{ |reason| raise reason } }
|
37
|
+
@on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } }
|
38
|
+
@on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } }
|
41
39
|
|
42
|
-
@promise_body = block || Proc.new{|result| result }
|
40
|
+
@promise_body = block || Proc.new { |result| result }
|
43
41
|
@state = :unscheduled
|
44
42
|
@children = []
|
45
43
|
|
@@ -48,13 +46,13 @@ module Concurrent
|
|
48
46
|
|
49
47
|
# @return [Promise]
|
50
48
|
def self.fulfill(value, opts = {})
|
51
|
-
Promise.new(opts).tap{ |p| p.send(:synchronized_set_state!, true, value, nil) }
|
49
|
+
Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) }
|
52
50
|
end
|
53
51
|
|
54
52
|
|
55
53
|
# @return [Promise]
|
56
54
|
def self.reject(reason, opts = {})
|
57
|
-
Promise.new(opts).tap{ |p| p.send(:synchronized_set_state!, false, nil, reason) }
|
55
|
+
Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) }
|
58
56
|
end
|
59
57
|
|
60
58
|
# @return [Promise]
|
@@ -79,7 +77,7 @@ module Concurrent
|
|
79
77
|
# @return [Promise] the new promise
|
80
78
|
def then(rescuer = nil, &block)
|
81
79
|
raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
|
82
|
-
block = Proc.new{ |result| result } if block.nil?
|
80
|
+
block = Proc.new { |result| result } if block.nil?
|
83
81
|
child = Promise.new(
|
84
82
|
parent: self,
|
85
83
|
executor: @executor,
|
@@ -107,6 +105,7 @@ module Concurrent
|
|
107
105
|
def rescue(&block)
|
108
106
|
self.then(block)
|
109
107
|
end
|
108
|
+
|
110
109
|
alias_method :catch, :rescue
|
111
110
|
alias_method :on_error, :rescue
|
112
111
|
|
@@ -126,13 +125,13 @@ module Concurrent
|
|
126
125
|
|
127
126
|
# @!visibility private
|
128
127
|
def on_fulfill(result)
|
129
|
-
realize Proc.new{ @on_fulfill.call(result) }
|
128
|
+
realize Proc.new { @on_fulfill.call(result) }
|
130
129
|
nil
|
131
130
|
end
|
132
131
|
|
133
132
|
# @!visibility private
|
134
133
|
def on_reject(reason)
|
135
|
-
realize Proc.new{ @on_reject.call(reason) }
|
134
|
+
realize Proc.new { @on_reject.call(reason) }
|
136
135
|
nil
|
137
136
|
end
|
138
137
|
|
@@ -144,14 +143,14 @@ module Concurrent
|
|
144
143
|
# @!visibility private
|
145
144
|
def realize(task)
|
146
145
|
@executor.post do
|
147
|
-
success, value, reason = SafeTaskExecutor.new(
|
146
|
+
success, value, reason = SafeTaskExecutor.new(task).execute
|
148
147
|
|
149
148
|
children_to_notify = mutex.synchronize do
|
150
149
|
set_state!(success, value, reason)
|
151
150
|
@children.dup
|
152
151
|
end
|
153
152
|
|
154
|
-
children_to_notify.each{ |child| notify_child(child) }
|
153
|
+
children_to_notify.each { |child| notify_child(child) }
|
155
154
|
end
|
156
155
|
end
|
157
156
|
|
@@ -161,9 +160,9 @@ module Concurrent
|
|
161
160
|
end
|
162
161
|
|
163
162
|
def synchronized_set_state!(success, value, reason)
|
164
|
-
mutex.
|
165
|
-
|
166
|
-
|
163
|
+
mutex.lock
|
164
|
+
set_state!(success, value, reason)
|
165
|
+
mutex.unlock
|
167
166
|
end
|
168
167
|
end
|
169
168
|
end
|
@@ -1,41 +1,37 @@
|
|
1
|
-
require '
|
2
|
-
require 'concurrent/
|
3
|
-
require 'concurrent/safe_task_executor'
|
1
|
+
require 'concurrent/ivar'
|
2
|
+
require 'concurrent/utility/timer'
|
3
|
+
require 'concurrent/executor/safe_task_executor'
|
4
4
|
|
5
5
|
module Concurrent
|
6
6
|
|
7
|
-
class ScheduledTask
|
8
|
-
include Obligation
|
9
|
-
|
10
|
-
SchedulingError = Class.new(ArgumentError)
|
7
|
+
class ScheduledTask < IVar
|
11
8
|
|
12
9
|
attr_reader :schedule_time
|
13
10
|
|
14
|
-
def initialize(
|
15
|
-
raise
|
16
|
-
calculate_schedule_time
|
11
|
+
def initialize(intended_time, opts = {}, &block)
|
12
|
+
raise ArgumentError.new('no block given') unless block_given?
|
13
|
+
TimerSet.calculate_schedule_time(intended_time) # raises exceptons
|
14
|
+
|
15
|
+
super(NO_VALUE, opts)
|
17
16
|
|
18
|
-
|
19
|
-
@
|
17
|
+
self.observers = CopyOnNotifyObserverSet.new
|
18
|
+
@intended_time = intended_time
|
20
19
|
@state = :unscheduled
|
21
|
-
@intended_schedule_time = schedule_time
|
22
|
-
@schedule_time = nil
|
23
20
|
@task = block
|
24
|
-
set_deref_options(opts)
|
25
21
|
end
|
26
22
|
|
27
23
|
# @since 0.5.0
|
28
24
|
def execute
|
29
25
|
if compare_and_set_state(:pending, :unscheduled)
|
30
|
-
@schedule_time = calculate_schedule_time
|
31
|
-
|
26
|
+
@schedule_time = TimerSet.calculate_schedule_time(@intended_time)
|
27
|
+
Concurrent::timer(@schedule_time.to_f - Time.now.to_f, &method(:process_task))
|
32
28
|
self
|
33
29
|
end
|
34
30
|
end
|
35
31
|
|
36
32
|
# @since 0.5.0
|
37
|
-
def self.execute(
|
38
|
-
return ScheduledTask.new(
|
33
|
+
def self.execute(intended_time, opts = {}, &block)
|
34
|
+
return ScheduledTask.new(intended_time, opts, &block).execute
|
39
35
|
end
|
40
36
|
|
41
37
|
def cancelled?
|
@@ -53,20 +49,19 @@ module Concurrent
|
|
53
49
|
true
|
54
50
|
end
|
55
51
|
end
|
56
|
-
|
57
52
|
alias_method :stop, :cancel
|
58
53
|
|
59
|
-
def add_observer(
|
54
|
+
def add_observer(*args, &block)
|
60
55
|
if_state(:unscheduled, :pending, :in_progress) do
|
61
|
-
|
56
|
+
observers.add_observer(*args, &block)
|
62
57
|
end
|
63
58
|
end
|
64
59
|
|
65
|
-
protected
|
60
|
+
protected :set, :fail, :complete
|
66
61
|
|
67
|
-
|
68
|
-
sleep_until_scheduled_time
|
62
|
+
private
|
69
63
|
|
64
|
+
def process_task
|
70
65
|
if compare_and_set_state(:in_progress, :pending)
|
71
66
|
success, val, reason = SafeTaskExecutor.new(@task).execute
|
72
67
|
|
@@ -76,26 +71,7 @@ module Concurrent
|
|
76
71
|
end
|
77
72
|
|
78
73
|
time = Time.now
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def sleep_until_scheduled_time
|
87
|
-
while (diff = @schedule_time.to_f - Time.now.to_f) > 0
|
88
|
-
sleep(diff > 60 ? 60 : diff)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def calculate_schedule_time!(schedule_time, now = Time.now)
|
93
|
-
if schedule_time.is_a?(Time)
|
94
|
-
raise SchedulingError.new('schedule time must be in the future') if schedule_time <= now
|
95
|
-
schedule_time.dup
|
96
|
-
else
|
97
|
-
raise SchedulingError.new('seconds must be greater than zero') if schedule_time.to_f <= 0.0
|
98
|
-
now + schedule_time.to_f
|
74
|
+
observers.notify_and_delete_observers{ [time, self.value, reason] }
|
99
75
|
end
|
100
76
|
end
|
101
77
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'observer'
|
3
|
-
|
4
1
|
require 'concurrent/dereferenceable'
|
2
|
+
require 'concurrent/observable'
|
3
|
+
require 'concurrent/atomic/atomic_boolean'
|
4
|
+
require 'concurrent/executor/executor'
|
5
|
+
require 'concurrent/executor/safe_task_executor'
|
6
|
+
|
7
|
+
# deprecated Updated to use `Executor` instead of `Runnable`
|
5
8
|
require 'concurrent/runnable'
|
6
|
-
require 'concurrent/stoppable'
|
7
|
-
require 'concurrent/utilities'
|
8
9
|
|
9
10
|
module Concurrent
|
10
11
|
|
@@ -15,35 +16,35 @@ module Concurrent
|
|
15
16
|
# task itself is tightly coupled with the concurrency logic. Second, an exception in
|
16
17
|
# raised while performing the task can cause the entire thread to abend. In a
|
17
18
|
# long-running application where the task thread is intended to run for days/weeks/years
|
18
|
-
# a crashed task thread can pose a significant problem.
|
19
|
+
# a crashed task thread can pose a significant problem. `TimerTask` alleviates both problems.
|
19
20
|
#
|
20
|
-
# When a
|
21
|
-
# The
|
21
|
+
# When a `TimerTask` is launched it starts a thread for monitoring the execution interval.
|
22
|
+
# The `TimerTask` thread does not perform the task, however. Instead, the TimerTask
|
22
23
|
# launches the task on a separate thread. Should the task experience an unrecoverable
|
23
|
-
# crash only the task thread will crash. This makes the
|
24
|
-
# Additionally, the
|
25
|
-
# performing logging or ancillary operations.
|
24
|
+
# crash only the task thread will crash. This makes the `TimerTask` very fault tolerant
|
25
|
+
# Additionally, the `TimerTask` thread can respond to the success or failure of the task,
|
26
|
+
# performing logging or ancillary operations. `TimerTask` can also be configured with a
|
26
27
|
# timeout value allowing it to kill a task that runs too long.
|
27
28
|
#
|
28
|
-
# One other advantage of
|
29
|
+
# One other advantage of `TimerTask` is it forces the business logic to be completely decoupled
|
29
30
|
# from the concurrency logic. The business logic can be tested separately then passed to the
|
30
|
-
#
|
31
|
+
# `TimerTask` for scheduling and running.
|
31
32
|
#
|
32
|
-
# In some cases it may be necessary for a
|
33
|
+
# In some cases it may be necessary for a `TimerTask` to affect its own execution cycle.
|
33
34
|
# To facilitate this a reference to the task object is passed into the block as a block
|
34
35
|
# argument every time the task is executed.
|
35
36
|
#
|
36
|
-
# The
|
37
|
-
# the last execution is always available via the
|
38
|
-
# can be passed to the
|
39
|
-
#
|
37
|
+
# The `TimerTask` class includes the `Dereferenceable` mixin module so the result of
|
38
|
+
# the last execution is always available via the `#value` method. Derefencing options
|
39
|
+
# can be passed to the `TimerTask` during construction or at any later time using the
|
40
|
+
# `#set_deref_options` method.
|
40
41
|
#
|
41
|
-
#
|
42
|
+
# `TimerTask` supports notification through the Ruby standard library
|
42
43
|
# {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html Observable}
|
43
|
-
# module. On execution the
|
44
|
+
# module. On execution the `TimerTask` will notify the observers
|
44
45
|
# with threes arguments: time of execution, the result of the block (or nil on failure),
|
45
46
|
# and any raised exceptions (or nil on success). If the timeout interval is exceeded
|
46
|
-
# the observer will receive a
|
47
|
+
# the observer will receive a `Concurrent::TimeoutError` object as the third argument.
|
47
48
|
#
|
48
49
|
# @example Basic usage
|
49
50
|
# task = Concurrent::TimerTask.new{ puts 'Boom!' }
|
@@ -57,7 +58,7 @@ module Concurrent
|
|
57
58
|
#
|
58
59
|
# task.stop #=> true
|
59
60
|
#
|
60
|
-
# @example Configuring
|
61
|
+
# @example Configuring `:execution_interval` and `:timeout_interval`
|
61
62
|
# task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do
|
62
63
|
# puts 'Boom!'
|
63
64
|
# end
|
@@ -65,13 +66,13 @@ module Concurrent
|
|
65
66
|
# task.execution_interval #=> 5
|
66
67
|
# task.timeout_interval #=> 5
|
67
68
|
#
|
68
|
-
# @example Immediate execution with
|
69
|
+
# @example Immediate execution with `:run_now`
|
69
70
|
# task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
|
70
71
|
# task.run!
|
71
72
|
#
|
72
73
|
# #=> 'Boom!'
|
73
74
|
#
|
74
|
-
# @example Last
|
75
|
+
# @example Last `#value` and `Dereferenceable` mixin
|
75
76
|
# task = Concurrent::TimerTask.new(
|
76
77
|
# dup_on_deref: true,
|
77
78
|
# execution_interval: 5
|
@@ -145,151 +146,228 @@ module Concurrent
|
|
145
146
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html
|
146
147
|
class TimerTask
|
147
148
|
include Dereferenceable
|
148
|
-
include
|
149
|
-
include
|
149
|
+
include Executor
|
150
|
+
include Concurrent::Observable
|
150
151
|
|
151
|
-
# Default
|
152
|
+
# Default `:execution_interval` in seconds.
|
152
153
|
EXECUTION_INTERVAL = 60
|
153
154
|
|
154
|
-
# Default
|
155
|
+
# Default `:timeout_interval` in seconds.
|
155
156
|
TIMEOUT_INTERVAL = 30
|
156
157
|
|
157
|
-
# Number of seconds after the task completes before the task is
|
158
|
-
# performed again.
|
159
|
-
attr_reader :execution_interval
|
160
|
-
|
161
|
-
# Number of seconds the task can run before it is considered to have failed.
|
162
|
-
# Failed tasks are forcibly killed.
|
163
|
-
attr_reader :timeout_interval
|
164
|
-
|
165
158
|
# Create a new TimerTask with the given task and configuration.
|
166
159
|
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
179
|
-
# the
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
160
|
+
# @!macro [attach] timer_task_initialize
|
161
|
+
# @param [Hash] opts the options defining task execution.
|
162
|
+
# @option opts [Integer] :execution_interval number of seconds between
|
163
|
+
# task executions (default: EXECUTION_INTERVAL)
|
164
|
+
# @option opts [Integer] :timeout_interval number of seconds a task can
|
165
|
+
# run before it is considered to have failed (default: TIMEOUT_INTERVAL)
|
166
|
+
# @option opts [Boolean] :run_now Whether to run the task immediately
|
167
|
+
# upon instantiation or to wait until the first # execution_interval
|
168
|
+
# has passed (default: false)
|
169
|
+
#
|
170
|
+
# @raise ArgumentError when no block is given.
|
171
|
+
#
|
172
|
+
# @yield to the block after :execution_interval seconds have passed since
|
173
|
+
# the last yield
|
174
|
+
# @yieldparam task a reference to the `TimerTask` instance so that the
|
175
|
+
# block can control its own lifecycle. Necessary since `self` will
|
176
|
+
# refer to the execution context of the block rather than the running
|
177
|
+
# `TimerTask`.
|
178
|
+
#
|
179
|
+
# @note Calls Concurrent::Dereferenceable# set_deref_options passing `opts`.
|
180
|
+
# All options supported by Concurrent::Dereferenceable can be set
|
181
|
+
# during object initialization.
|
184
182
|
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
|
189
|
-
# @see Concurrent::Dereferenceable#set_deref_options
|
190
|
-
def initialize(opts = {}, &block)
|
183
|
+
# @return [TimerTask] the new `TimerTask`
|
184
|
+
#
|
185
|
+
# @see Concurrent::Dereferenceable# set_deref_options
|
186
|
+
def initialize(opts = {}, &task)
|
191
187
|
raise ArgumentError.new('no block given') unless block_given?
|
192
188
|
|
189
|
+
init_executor
|
190
|
+
set_deref_options(opts)
|
191
|
+
|
193
192
|
self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
194
193
|
self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
|
195
194
|
@run_now = opts[:now] || opts[:run_now]
|
195
|
+
@executor = Concurrent::SafeTaskExecutor.new(task)
|
196
|
+
@running = Concurrent::AtomicBoolean.new(false)
|
196
197
|
|
197
|
-
|
198
|
-
@observers = CopyOnWriteObserverSet.new
|
199
|
-
init_mutex
|
200
|
-
set_deref_options(opts)
|
198
|
+
self.observers = CopyOnNotifyObserverSet.new
|
201
199
|
end
|
202
200
|
|
203
|
-
#
|
204
|
-
# performed again.
|
201
|
+
# Is the executor running?
|
205
202
|
#
|
206
|
-
# @
|
203
|
+
# @return [Boolean] `true` when running, `false` when shutting down or shutdown
|
204
|
+
def running?
|
205
|
+
@running.true?
|
206
|
+
end
|
207
|
+
|
208
|
+
# Execute a previously created `TimerTask`.
|
207
209
|
#
|
208
|
-
# @
|
209
|
-
|
210
|
-
|
211
|
-
|
210
|
+
# @return [TimerTask] a reference to `self`
|
211
|
+
#
|
212
|
+
# @example Instance and execute in separate steps
|
213
|
+
# task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }
|
214
|
+
# task.running? #=> false
|
215
|
+
# task.execute
|
216
|
+
# task.running? #=> true
|
217
|
+
#
|
218
|
+
# @example Instance and execute in one line
|
219
|
+
# task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute
|
220
|
+
# task.running? #=> true
|
221
|
+
#
|
222
|
+
# @since 0.6.0
|
223
|
+
def execute
|
224
|
+
mutex.synchronize do
|
225
|
+
if @running.false?
|
226
|
+
@running.make_true
|
227
|
+
schedule_next_task(@run_now ? 0 : @execution_interval)
|
228
|
+
end
|
212
229
|
end
|
213
|
-
|
230
|
+
self
|
214
231
|
end
|
215
232
|
|
216
|
-
#
|
217
|
-
# Failed tasks are forcibly killed.
|
233
|
+
# Create and execute a new `TimerTask`.
|
218
234
|
#
|
219
|
-
#
|
235
|
+
# @!macro timer_task_initialize
|
220
236
|
#
|
221
|
-
# @
|
222
|
-
|
237
|
+
# @example
|
238
|
+
# task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" }
|
239
|
+
# task.running? #=> true
|
240
|
+
#
|
241
|
+
# @since 0.6.0
|
242
|
+
def self.execute(opts = {}, &task)
|
243
|
+
TimerTask.new(opts, &task).execute
|
244
|
+
end
|
245
|
+
|
246
|
+
# @!attribute [rw] execution_interval
|
247
|
+
# @return [Fixnum] Number of seconds after the task completes before the
|
248
|
+
# task is performed again.
|
249
|
+
def execution_interval
|
250
|
+
mutex.lock
|
251
|
+
result = @execution_interval
|
252
|
+
mutex.unlock
|
253
|
+
result
|
254
|
+
end
|
255
|
+
|
256
|
+
# @!attribute [rw] execution_interval
|
257
|
+
# @return [Fixnum] Number of seconds after the task completes before the
|
258
|
+
# task is performed again.
|
259
|
+
def execution_interval=(value)
|
223
260
|
if (value = value.to_f) <= 0.0
|
224
|
-
raise ArgumentError.new(
|
261
|
+
raise ArgumentError.new('must be greater than zero')
|
262
|
+
else
|
263
|
+
mutex.lock
|
264
|
+
result = @execution_interval = value
|
265
|
+
mutex.unlock
|
266
|
+
result
|
225
267
|
end
|
226
|
-
@timeout_interval = value
|
227
268
|
end
|
228
269
|
|
229
|
-
|
230
|
-
|
270
|
+
# @!attribute [rw] timeout_interval
|
271
|
+
# @return [Fixnum] Number of seconds the task can run before it is
|
272
|
+
# considered to have failed.
|
273
|
+
def timeout_interval
|
274
|
+
mutex.lock
|
275
|
+
result = @timeout_interval
|
276
|
+
mutex.unlock
|
277
|
+
result
|
231
278
|
end
|
232
279
|
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
Thread.kill(@monitor) unless @monitor.nil?
|
280
|
+
# @!attribute [rw] timeout_interval
|
281
|
+
# @return [Fixnum] Number of seconds the task can run before it is
|
282
|
+
# considered to have failed.
|
283
|
+
def timeout_interval=(value)
|
284
|
+
if (value = value.to_f) <= 0.0
|
285
|
+
raise ArgumentError.new('must be greater than zero')
|
286
|
+
else
|
287
|
+
mutex.lock
|
288
|
+
result = @timeout_interval = value
|
289
|
+
mutex.unlock
|
290
|
+
result
|
245
291
|
end
|
246
|
-
return true
|
247
|
-
rescue
|
248
|
-
return false
|
249
|
-
ensure
|
250
|
-
@worker = @monitor = nil
|
251
292
|
end
|
252
|
-
alias_method :terminate, :kill
|
253
293
|
|
254
|
-
|
294
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
295
|
+
def terminate(*args) deprecated(:terminate, :kill, *args); end
|
296
|
+
|
297
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
298
|
+
def stop(*args) deprecated(:stop, :shutdown, *args); end
|
299
|
+
|
300
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
301
|
+
def cancel(*args) deprecated(:cancel, :shutdown, *args); end
|
302
|
+
|
303
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
304
|
+
def run!(*args) deprecated(:run!, :execute); end
|
305
|
+
|
306
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
307
|
+
def self.run!(*args, &block)
|
308
|
+
warn "[DEPRECATED] `run!` is deprecated, please use `execute` instead."
|
309
|
+
Concurrent::Runnable::Context.new(TimerTask.new(*args, &block))
|
310
|
+
end
|
311
|
+
|
312
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
313
|
+
def run
|
314
|
+
raise Concurrent::Runnable::LifecycleError.new('already running') if @running.true?
|
315
|
+
self.execute
|
316
|
+
self.wait_for_termination
|
317
|
+
true
|
318
|
+
end
|
319
|
+
|
320
|
+
private :post, :<<
|
255
321
|
|
256
322
|
protected
|
257
323
|
|
258
|
-
|
259
|
-
|
324
|
+
# @!visibility private
|
325
|
+
def shutdown_execution
|
326
|
+
@running.make_false
|
327
|
+
super
|
260
328
|
end
|
261
329
|
|
262
|
-
|
263
|
-
|
264
|
-
@
|
265
|
-
|
330
|
+
# @!visibility private
|
331
|
+
def kill_execution
|
332
|
+
@running.make_false
|
333
|
+
super
|
266
334
|
end
|
267
335
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
else
|
272
|
-
sleep(@execution_interval)
|
273
|
-
end
|
274
|
-
execute_task
|
336
|
+
# @!visibility private
|
337
|
+
def schedule_next_task(interval = execution_interval)
|
338
|
+
Concurrent::timer(interval, Concurrent::Event.new, &method(:execute_task))
|
275
339
|
end
|
276
340
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
341
|
+
# @!visibility private
|
342
|
+
def execute_task(completion)
|
343
|
+
return unless @running.true?
|
344
|
+
Concurrent::timer(timeout_interval, completion, &method(:timeout_task))
|
345
|
+
success, value, reason = @executor.execute(self)
|
346
|
+
if completion.try?
|
347
|
+
self.value = value
|
348
|
+
schedule_next_task
|
349
|
+
time = Time.now
|
350
|
+
observers.notify_observers do
|
351
|
+
[time, self.value, reason]
|
352
|
+
end
|
282
353
|
end
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
354
|
+
end
|
355
|
+
|
356
|
+
# @!visibility private
|
357
|
+
def timeout_task(completion)
|
358
|
+
return unless @running.true?
|
359
|
+
if completion.try?
|
360
|
+
self.value = value
|
361
|
+
schedule_next_task
|
362
|
+
observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new)
|
292
363
|
end
|
293
364
|
end
|
365
|
+
|
366
|
+
# @deprecated Updated to use `Executor` instead of `Runnable`
|
367
|
+
# @!visibility private
|
368
|
+
def deprecated(old, new, *args)
|
369
|
+
warn "[DEPRECATED] `#{old}` is deprecated, please use `#{new}` instead."
|
370
|
+
self.send(new, *args)
|
371
|
+
end
|
294
372
|
end
|
295
373
|
end
|