concurrent-ruby 0.8.0 → 0.9.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -2
- data/README.md +103 -54
- data/lib/concurrent.rb +34 -14
- data/lib/concurrent/async.rb +164 -50
- data/lib/concurrent/atom.rb +171 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +27 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +25 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +226 -112
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +10 -0
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +70 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +208 -134
- data/lib/concurrent/scheduled_task.rb +339 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +35 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +24 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +87 -100
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +40 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- metadata +47 -83
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -52
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
@@ -0,0 +1,24 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
class MonitorObject < MutexObject
|
7
|
+
def initialize
|
8
|
+
@__lock__ = ::Monitor.new
|
9
|
+
@__condition__ = @__lock__.new_cond
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def synchronize
|
15
|
+
@__lock__.synchronize { yield }
|
16
|
+
end
|
17
|
+
|
18
|
+
def ns_wait(timeout = nil)
|
19
|
+
@__condition__.wait timeout
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
class MutexObject < AbstractObject
|
7
|
+
def initialize
|
8
|
+
@__lock__ = ::Mutex.new
|
9
|
+
@__condition__ = ::ConditionVariable.new
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def synchronize
|
15
|
+
if @__lock__.owned?
|
16
|
+
yield
|
17
|
+
else
|
18
|
+
@__lock__.synchronize { yield }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def ns_signal
|
23
|
+
@__condition__.signal
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def ns_broadcast
|
28
|
+
@__condition__.broadcast
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def ns_wait(timeout = nil)
|
33
|
+
@__condition__.wait @__lock__, timeout
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def ensure_ivar_visibility!
|
38
|
+
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
39
|
+
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
Implementation = case
|
7
|
+
when Concurrent.on_jruby?
|
8
|
+
JavaObject
|
9
|
+
when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
|
10
|
+
MonitorObject
|
11
|
+
when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
|
12
|
+
MutexObject
|
13
|
+
when Concurrent.on_rbx?
|
14
|
+
RbxObject
|
15
|
+
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
17
|
+
MutexObject
|
18
|
+
end
|
19
|
+
private_constant :Implementation
|
20
|
+
|
21
|
+
# @!macro [attach] synchronization_object
|
22
|
+
#
|
23
|
+
# Safe synchronization under any Ruby implementation.
|
24
|
+
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
25
|
+
# Provides a single layer which can improve its implementation over time without changes needed to
|
26
|
+
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
27
|
+
#
|
28
|
+
# @note this object does not support usage together with
|
29
|
+
# [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
|
30
|
+
# and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
|
31
|
+
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
32
|
+
# `Thread#wakeup` will not work on all platforms.
|
33
|
+
#
|
34
|
+
# @see {Event} implementation as an example of this class use
|
35
|
+
#
|
36
|
+
# @example simple
|
37
|
+
# class AnClass < Synchronization::Object
|
38
|
+
# def initialize
|
39
|
+
# super
|
40
|
+
# synchronize { @value = 'asd' }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def value
|
44
|
+
# synchronize { @value }
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
class Object < Implementation
|
49
|
+
|
50
|
+
# @!method initialize(*args, &block)
|
51
|
+
# @!macro synchronization_object_method_initialize
|
52
|
+
|
53
|
+
# @!method synchronize
|
54
|
+
# @!macro synchronization_object_method_synchronize
|
55
|
+
|
56
|
+
# @!method initialize(*args, &block)
|
57
|
+
# @!macro synchronization_object_method_ns_initialize
|
58
|
+
|
59
|
+
# @!method wait_until(timeout = nil, &condition)
|
60
|
+
# @!macro synchronization_object_method_ns_wait_until
|
61
|
+
|
62
|
+
# @!method wait(timeout = nil)
|
63
|
+
# @!macro synchronization_object_method_ns_wait
|
64
|
+
|
65
|
+
# @!method signal
|
66
|
+
# @!macro synchronization_object_method_ns_signal
|
67
|
+
|
68
|
+
# @!method broadcast
|
69
|
+
# @!macro synchronization_object_method_ns_broadcast
|
70
|
+
|
71
|
+
# @!method ensure_ivar_visibility!
|
72
|
+
# @!macro synchronization_object_method_ensure_ivar_visibility
|
73
|
+
|
74
|
+
# @!method self.attr_volatile(*names)
|
75
|
+
# @!macro synchronization_object_method_self_attr_volatile
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
if Concurrent.on_rbx?
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
# @!macro internal_implementation_note
|
8
|
+
class RbxObject < AbstractObject
|
9
|
+
def initialize
|
10
|
+
@Waiters = []
|
11
|
+
ensure_ivar_visibility!
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def synchronize(&block)
|
17
|
+
Rubinius.synchronize(self, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ns_wait(timeout = nil)
|
21
|
+
wchan = Rubinius::Channel.new
|
22
|
+
|
23
|
+
begin
|
24
|
+
@Waiters.push wchan
|
25
|
+
Rubinius.unlock(self)
|
26
|
+
signaled = wchan.receive_timeout timeout
|
27
|
+
ensure
|
28
|
+
Rubinius.lock(self)
|
29
|
+
|
30
|
+
if !signaled && !@Waiters.delete(wchan)
|
31
|
+
# we timed out, but got signaled afterwards,
|
32
|
+
# so pass that signal on to the next waiter
|
33
|
+
@Waiters.shift << true unless @Waiters.empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def ns_signal
|
41
|
+
@Waiters.shift << true unless @Waiters.empty?
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def ns_broadcast
|
46
|
+
@Waiters.shift << true until @Waiters.empty?
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def ensure_ivar_visibility!
|
51
|
+
# Rubinius instance variables are not volatile so we need to insert barrier
|
52
|
+
Rubinius.memory_barrier
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.attr_volatile *names
|
56
|
+
names.each do |name|
|
57
|
+
ivar = :"@volatile_#{name}"
|
58
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
59
|
+
def #{name}
|
60
|
+
Rubinius.memory_barrier
|
61
|
+
#{ivar}
|
62
|
+
end
|
63
|
+
|
64
|
+
def #{name}=(value)
|
65
|
+
#{ivar} = value
|
66
|
+
Rubinius.memory_barrier
|
67
|
+
end
|
68
|
+
RUBY
|
69
|
+
end
|
70
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,72 +1,81 @@
|
|
1
|
-
require 'concurrent/
|
2
|
-
require 'concurrent/
|
1
|
+
require 'concurrent/collection/copy_on_notify_observer_set'
|
2
|
+
require 'concurrent/concern/dereferenceable'
|
3
|
+
require 'concurrent/concern/observable'
|
3
4
|
require 'concurrent/atomic/atomic_boolean'
|
4
|
-
require 'concurrent/executor/
|
5
|
+
require 'concurrent/executor/executor_service'
|
5
6
|
require 'concurrent/executor/safe_task_executor'
|
6
7
|
|
7
8
|
module Concurrent
|
8
9
|
|
9
|
-
# A very common currency pattern is to run a thread that performs a task at
|
10
|
-
# intervals. The thread that performs the task sleeps for the given
|
11
|
-
# wakes up and performs the task. Lather, rinse, repeat... This
|
12
|
-
# problems. First, it is difficult to test the business
|
13
|
-
# task itself is tightly coupled with the
|
14
|
-
# raised while performing the task can
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
10
|
+
# A very common currency pattern is to run a thread that performs a task at
|
11
|
+
# regular intervals. The thread that performs the task sleeps for the given
|
12
|
+
# interval then wakes up and performs the task. Lather, rinse, repeat... This
|
13
|
+
# pattern causes two problems. First, it is difficult to test the business
|
14
|
+
# logic of the task because the task itself is tightly coupled with the
|
15
|
+
# concurrency logic. Second, an exception raised while performing the task can
|
16
|
+
# cause the entire thread to abend. In a long-running application where the
|
17
|
+
# task thread is intended to run for days/weeks/years a crashed task thread
|
18
|
+
# can pose a significant problem. `TimerTask` alleviates both problems.
|
19
|
+
#
|
20
|
+
# When a `TimerTask` is launched it starts a thread for monitoring the
|
21
|
+
# execution interval. The `TimerTask` thread does not perform the task,
|
22
|
+
# however. Instead, the TimerTask launches the task on a separate thread.
|
23
|
+
# Should the task experience an unrecoverable crash only the task thread will
|
24
|
+
# crash. This makes the `TimerTask` very fault tolerant Additionally, the
|
25
|
+
# `TimerTask` thread can respond to the success or failure of the task,
|
26
|
+
# performing logging or ancillary operations. `TimerTask` can also be
|
27
|
+
# configured with a timeout value allowing it to kill a task that runs too
|
28
|
+
# long.
|
29
|
+
#
|
30
|
+
# One other advantage of `TimerTask` is that it forces the business logic to
|
31
|
+
# be completely decoupled from the concurrency logic. The business logic can
|
32
|
+
# be tested separately then passed to the `TimerTask` for scheduling and
|
33
|
+
# running.
|
34
|
+
#
|
35
|
+
# In some cases it may be necessary for a `TimerTask` to affect its own
|
36
|
+
# execution cycle. To facilitate this, a reference to the TimerTask instance
|
37
|
+
# is passed as an argument to the provided block every time the task is
|
38
|
+
# executed.
|
39
|
+
#
|
40
|
+
# The `TimerTask` class includes the `Dereferenceable` mixin module so the
|
41
|
+
# result of the last execution is always available via the `#value` method.
|
42
|
+
# Derefencing options can be passed to the `TimerTask` during construction or
|
43
|
+
# at any later time using the `#set_deref_options` method.
|
44
|
+
#
|
39
45
|
# `TimerTask` supports notification through the Ruby standard library
|
40
|
-
# {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
|
41
|
-
# module. On execution the `TimerTask` will notify the observers
|
42
|
-
# with three arguments: time of execution, the result of the block (or nil on
|
43
|
-
# and any raised exceptions (or nil on success). If the timeout
|
44
|
-
# the observer will receive a `Concurrent::TimeoutError`
|
46
|
+
# {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
|
47
|
+
# Observable} module. On execution the `TimerTask` will notify the observers
|
48
|
+
# with three arguments: time of execution, the result of the block (or nil on
|
49
|
+
# failure), and any raised exceptions (or nil on success). If the timeout
|
50
|
+
# interval is exceeded the observer will receive a `Concurrent::TimeoutError`
|
51
|
+
# object as the third argument.
|
52
|
+
#
|
53
|
+
# @!macro copy_options
|
45
54
|
#
|
46
55
|
# @example Basic usage
|
47
56
|
# task = Concurrent::TimerTask.new{ puts 'Boom!' }
|
48
57
|
# task.execute
|
49
|
-
#
|
58
|
+
#
|
50
59
|
# task.execution_interval #=> 60 (default)
|
51
60
|
# task.timeout_interval #=> 30 (default)
|
52
|
-
#
|
61
|
+
#
|
53
62
|
# # wait 60 seconds...
|
54
63
|
# #=> 'Boom!'
|
55
|
-
#
|
64
|
+
#
|
56
65
|
# task.shutdown #=> true
|
57
66
|
#
|
58
67
|
# @example Configuring `:execution_interval` and `:timeout_interval`
|
59
68
|
# task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do
|
60
69
|
# puts 'Boom!'
|
61
70
|
# end
|
62
|
-
#
|
71
|
+
#
|
63
72
|
# task.execution_interval #=> 5
|
64
73
|
# task.timeout_interval #=> 5
|
65
74
|
#
|
66
75
|
# @example Immediate execution with `:run_now`
|
67
76
|
# task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
|
68
77
|
# task.execute
|
69
|
-
#
|
78
|
+
#
|
70
79
|
# #=> 'Boom!'
|
71
80
|
#
|
72
81
|
# @example Last `#value` and `Dereferenceable` mixin
|
@@ -74,7 +83,7 @@ module Concurrent
|
|
74
83
|
# dup_on_deref: true,
|
75
84
|
# execution_interval: 5
|
76
85
|
# ){ Time.now }
|
77
|
-
#
|
86
|
+
#
|
78
87
|
# task.execute
|
79
88
|
# Time.now #=> 2013-11-07 18:06:50 -0500
|
80
89
|
# sleep(10)
|
@@ -90,7 +99,7 @@ module Concurrent
|
|
90
99
|
# task.shutdown
|
91
100
|
# end
|
92
101
|
# end
|
93
|
-
#
|
102
|
+
#
|
94
103
|
# timer_task.execute # blocking call - this task will stop itself
|
95
104
|
# #=> Boom!
|
96
105
|
# #=> Boom! Boom!
|
@@ -111,29 +120,29 @@ module Concurrent
|
|
111
120
|
# end
|
112
121
|
# end
|
113
122
|
# end
|
114
|
-
#
|
123
|
+
#
|
115
124
|
# task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 }
|
116
125
|
# task.add_observer(TaskObserver.new)
|
117
126
|
# task.execute
|
118
|
-
#
|
127
|
+
#
|
119
128
|
# #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42
|
120
129
|
# #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42
|
121
130
|
# #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42
|
122
131
|
# task.shutdown
|
123
|
-
#
|
132
|
+
#
|
124
133
|
# task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ sleep }
|
125
134
|
# task.add_observer(TaskObserver.new)
|
126
135
|
# task.execute
|
127
|
-
#
|
136
|
+
#
|
128
137
|
# #=> (2013-10-13 19:07:25 -0400) Execution timed out
|
129
138
|
# #=> (2013-10-13 19:07:27 -0400) Execution timed out
|
130
139
|
# #=> (2013-10-13 19:07:29 -0400) Execution timed out
|
131
140
|
# task.shutdown
|
132
|
-
#
|
141
|
+
#
|
133
142
|
# task = Concurrent::TimerTask.new(execution_interval: 1){ raise StandardError }
|
134
143
|
# task.add_observer(TaskObserver.new)
|
135
144
|
# task.execute
|
136
|
-
#
|
145
|
+
#
|
137
146
|
# #=> (2013-10-13 19:09:37 -0400) Execution failed with error StandardError
|
138
147
|
# #=> (2013-10-13 19:09:38 -0400) Execution failed with error StandardError
|
139
148
|
# #=> (2013-10-13 19:09:39 -0400) Execution failed with error StandardError
|
@@ -141,10 +150,9 @@ module Concurrent
|
|
141
150
|
#
|
142
151
|
# @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
|
143
152
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html
|
144
|
-
class TimerTask
|
145
|
-
include Dereferenceable
|
146
|
-
include
|
147
|
-
include Concurrent::Observable
|
153
|
+
class TimerTask < RubyExecutorService
|
154
|
+
include Concern::Dereferenceable
|
155
|
+
include Concern::Observable
|
148
156
|
|
149
157
|
# Default `:execution_interval` in seconds.
|
150
158
|
EXECUTION_INTERVAL = 60
|
@@ -163,36 +171,22 @@ module Concurrent
|
|
163
171
|
# @option opts [Boolean] :run_now Whether to run the task immediately
|
164
172
|
# upon instantiation or to wait until the first # execution_interval
|
165
173
|
# has passed (default: false)
|
166
|
-
#
|
174
|
+
#
|
175
|
+
# @!macro deref_options
|
176
|
+
#
|
167
177
|
# @raise ArgumentError when no block is given.
|
168
|
-
#
|
178
|
+
#
|
169
179
|
# @yield to the block after :execution_interval seconds have passed since
|
170
180
|
# the last yield
|
171
181
|
# @yieldparam task a reference to the `TimerTask` instance so that the
|
172
182
|
# block can control its own lifecycle. Necessary since `self` will
|
173
183
|
# refer to the execution context of the block rather than the running
|
174
184
|
# `TimerTask`.
|
175
|
-
#
|
176
|
-
# @note Calls Concurrent::Dereferenceable# set_deref_options passing `opts`.
|
177
|
-
# All options supported by Concurrent::Dereferenceable can be set
|
178
|
-
# during object initialization.
|
179
185
|
#
|
180
186
|
# @return [TimerTask] the new `TimerTask`
|
181
|
-
#
|
182
|
-
# @see Concurrent::Dereferenceable# set_deref_options
|
183
187
|
def initialize(opts = {}, &task)
|
184
188
|
raise ArgumentError.new('no block given') unless block_given?
|
185
|
-
|
186
|
-
init_executor
|
187
|
-
set_deref_options(opts)
|
188
|
-
|
189
|
-
self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
190
|
-
self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
|
191
|
-
@run_now = opts[:now] || opts[:run_now]
|
192
|
-
@executor = Concurrent::SafeTaskExecutor.new(task)
|
193
|
-
@running = Concurrent::AtomicBoolean.new(false)
|
194
|
-
|
195
|
-
self.observers = CopyOnNotifyObserverSet.new
|
189
|
+
super
|
196
190
|
end
|
197
191
|
|
198
192
|
# Is the executor running?
|
@@ -215,10 +209,8 @@ module Concurrent
|
|
215
209
|
# @example Instance and execute in one line
|
216
210
|
# task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute
|
217
211
|
# task.running? #=> true
|
218
|
-
#
|
219
|
-
# @since 0.6.0
|
220
212
|
def execute
|
221
|
-
|
213
|
+
synchronize do
|
222
214
|
if @running.false?
|
223
215
|
@running.make_true
|
224
216
|
schedule_next_task(@run_now ? 0 : @execution_interval)
|
@@ -231,11 +223,9 @@ module Concurrent
|
|
231
223
|
#
|
232
224
|
# @!macro timer_task_initialize
|
233
225
|
#
|
234
|
-
# @example
|
226
|
+
# @example
|
235
227
|
# task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" }
|
236
228
|
# task.running? #=> true
|
237
|
-
#
|
238
|
-
# @since 0.6.0
|
239
229
|
def self.execute(opts = {}, &task)
|
240
230
|
TimerTask.new(opts, &task).execute
|
241
231
|
end
|
@@ -244,10 +234,7 @@ module Concurrent
|
|
244
234
|
# @return [Fixnum] Number of seconds after the task completes before the
|
245
235
|
# task is performed again.
|
246
236
|
def execution_interval
|
247
|
-
|
248
|
-
@execution_interval
|
249
|
-
ensure
|
250
|
-
mutex.unlock
|
237
|
+
synchronize { @execution_interval }
|
251
238
|
end
|
252
239
|
|
253
240
|
# @!attribute [rw] execution_interval
|
@@ -257,12 +244,7 @@ module Concurrent
|
|
257
244
|
if (value = value.to_f) <= 0.0
|
258
245
|
raise ArgumentError.new('must be greater than zero')
|
259
246
|
else
|
260
|
-
|
261
|
-
mutex.lock
|
262
|
-
@execution_interval = value
|
263
|
-
ensure
|
264
|
-
mutex.unlock
|
265
|
-
end
|
247
|
+
synchronize { @execution_interval = value }
|
266
248
|
end
|
267
249
|
end
|
268
250
|
|
@@ -270,10 +252,7 @@ module Concurrent
|
|
270
252
|
# @return [Fixnum] Number of seconds the task can run before it is
|
271
253
|
# considered to have failed.
|
272
254
|
def timeout_interval
|
273
|
-
|
274
|
-
@timeout_interval
|
275
|
-
ensure
|
276
|
-
mutex.unlock
|
255
|
+
synchronize { @timeout_interval }
|
277
256
|
end
|
278
257
|
|
279
258
|
# @!attribute [rw] timeout_interval
|
@@ -283,12 +262,7 @@ module Concurrent
|
|
283
262
|
if (value = value.to_f) <= 0.0
|
284
263
|
raise ArgumentError.new('must be greater than zero')
|
285
264
|
else
|
286
|
-
|
287
|
-
mutex.lock
|
288
|
-
@timeout_interval = value
|
289
|
-
ensure
|
290
|
-
mutex.unlock
|
291
|
-
end
|
265
|
+
synchronize { @timeout_interval = value }
|
292
266
|
end
|
293
267
|
end
|
294
268
|
|
@@ -296,6 +270,19 @@ module Concurrent
|
|
296
270
|
|
297
271
|
protected
|
298
272
|
|
273
|
+
def ns_initialize(opts, &task)
|
274
|
+
init_mutex(self)
|
275
|
+
set_deref_options(opts)
|
276
|
+
|
277
|
+
self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
278
|
+
self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
|
279
|
+
@run_now = opts[:now] || opts[:run_now]
|
280
|
+
@executor = Concurrent::SafeTaskExecutor.new(task)
|
281
|
+
@running = Concurrent::AtomicBoolean.new(false)
|
282
|
+
|
283
|
+
self.observers = Collection::CopyOnNotifyObserverSet.new
|
284
|
+
end
|
285
|
+
|
299
286
|
# @!visibility private
|
300
287
|
def shutdown_execution
|
301
288
|
@running.make_false
|