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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -2
  3. data/README.md +103 -54
  4. data/lib/concurrent.rb +34 -14
  5. data/lib/concurrent/async.rb +164 -50
  6. data/lib/concurrent/atom.rb +171 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +3 -0
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +27 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +25 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +226 -112
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +10 -0
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +46 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -26
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_cached_thread_pool.rb +18 -16
  46. data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
  47. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  48. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  49. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
  50. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
  51. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  52. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  53. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  54. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  55. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  56. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  57. data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
  58. data/lib/concurrent/executor/timer_set.rb +96 -84
  59. data/lib/concurrent/executors.rb +1 -1
  60. data/lib/concurrent/future.rb +70 -38
  61. data/lib/concurrent/immutable_struct.rb +89 -0
  62. data/lib/concurrent/ivar.rb +152 -60
  63. data/lib/concurrent/lazy_register.rb +40 -20
  64. data/lib/concurrent/maybe.rb +226 -0
  65. data/lib/concurrent/mutable_struct.rb +227 -0
  66. data/lib/concurrent/mvar.rb +44 -43
  67. data/lib/concurrent/promise.rb +208 -134
  68. data/lib/concurrent/scheduled_task.rb +339 -43
  69. data/lib/concurrent/settable_struct.rb +127 -0
  70. data/lib/concurrent/synchronization.rb +17 -0
  71. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  72. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  73. data/lib/concurrent/synchronization/condition.rb +53 -0
  74. data/lib/concurrent/synchronization/java_object.rb +35 -0
  75. data/lib/concurrent/synchronization/lock.rb +32 -0
  76. data/lib/concurrent/synchronization/monitor_object.rb +24 -0
  77. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  78. data/lib/concurrent/synchronization/object.rb +78 -0
  79. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  80. data/lib/concurrent/timer_task.rb +87 -100
  81. data/lib/concurrent/tvar.rb +42 -38
  82. data/lib/concurrent/utilities.rb +3 -1
  83. data/lib/concurrent/utility/at_exit.rb +97 -0
  84. data/lib/concurrent/utility/engine.rb +40 -0
  85. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  86. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  87. data/lib/concurrent/utility/processor_counter.rb +156 -0
  88. data/lib/concurrent/utility/timeout.rb +18 -14
  89. data/lib/concurrent/utility/timer.rb +11 -6
  90. data/lib/concurrent/version.rb +2 -1
  91. data/lib/concurrent_ruby.rb +1 -0
  92. metadata +47 -83
  93. data/lib/concurrent/actor.rb +0 -103
  94. data/lib/concurrent/actor/behaviour.rb +0 -70
  95. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  96. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  97. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  98. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  99. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  100. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  101. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  102. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  103. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  104. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  105. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  106. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  107. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  108. data/lib/concurrent/actor/context.rb +0 -154
  109. data/lib/concurrent/actor/core.rb +0 -217
  110. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  111. data/lib/concurrent/actor/envelope.rb +0 -41
  112. data/lib/concurrent/actor/errors.rb +0 -27
  113. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  114. data/lib/concurrent/actor/public_delegations.rb +0 -40
  115. data/lib/concurrent/actor/reference.rb +0 -81
  116. data/lib/concurrent/actor/root.rb +0 -37
  117. data/lib/concurrent/actor/type_check.rb +0 -48
  118. data/lib/concurrent/actor/utils.rb +0 -10
  119. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  120. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  121. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  122. data/lib/concurrent/actor/utils/pool.rb +0 -59
  123. data/lib/concurrent/actress.rb +0 -3
  124. data/lib/concurrent/agent.rb +0 -209
  125. data/lib/concurrent/atomic.rb +0 -92
  126. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  127. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  128. data/lib/concurrent/atomic/synchronization.rb +0 -51
  129. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  130. data/lib/concurrent/channel/channel.rb +0 -41
  131. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  132. data/lib/concurrent/channel/waitable_list.rb +0 -40
  133. data/lib/concurrent/channels.rb +0 -5
  134. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  135. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  136. data/lib/concurrent/collections.rb +0 -3
  137. data/lib/concurrent/dereferenceable.rb +0 -108
  138. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  139. data/lib/concurrent/logging.rb +0 -20
  140. data/lib/concurrent/obligation.rb +0 -171
  141. data/lib/concurrent/observable.rb +0 -73
  142. data/lib/concurrent/options_parser.rb +0 -52
  143. data/lib/concurrent/utility/processor_count.rb +0 -152
  144. 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/dereferenceable'
2
- require 'concurrent/observable'
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/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 regular
10
- # intervals. The thread that performs the task sleeps for the given interval then
11
- # wakes up and performs the task. Lather, rinse, repeat... This pattern causes two
12
- # problems. First, it is difficult to test the business logic of the task because the
13
- # task itself is tightly coupled with the concurrency logic. Second, an exception
14
- # raised while performing the task can cause the entire thread to abend. In a
15
- # long-running application where the task thread is intended to run for days/weeks/years
16
- # a crashed task thread can pose a significant problem. `TimerTask` alleviates both problems.
17
- #
18
- # When a `TimerTask` is launched it starts a thread for monitoring the execution interval.
19
- # The `TimerTask` thread does not perform the task, however. Instead, the TimerTask
20
- # launches the task on a separate thread. Should the task experience an unrecoverable
21
- # crash only the task thread will crash. This makes the `TimerTask` very fault tolerant
22
- # Additionally, the `TimerTask` thread can respond to the success or failure of the task,
23
- # performing logging or ancillary operations. `TimerTask` can also be configured with a
24
- # timeout value allowing it to kill a task that runs too long.
25
- #
26
- # One other advantage of `TimerTask` is that it forces the business logic to be completely decoupled
27
- # from the concurrency logic. The business logic can be tested separately then passed to the
28
- # `TimerTask` for scheduling and running.
29
- #
30
- # In some cases it may be necessary for a `TimerTask` to affect its own execution cycle.
31
- # To facilitate this, a reference to the TimerTask instance is passed as an argument
32
- # to the provided block every time the task is executed.
33
- #
34
- # The `TimerTask` class includes the `Dereferenceable` mixin module so the result of
35
- # the last execution is always available via the `#value` method. Derefencing options
36
- # can be passed to the `TimerTask` during construction or at any later time using the
37
- # `#set_deref_options` method.
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 Observable}
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 failure),
43
- # and any raised exceptions (or nil on success). If the timeout interval is exceeded
44
- # the observer will receive a `Concurrent::TimeoutError` object as the third argument.
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 RubyExecutor
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
- mutex.synchronize do
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
- mutex.lock
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
- begin
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
- mutex.lock
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
- begin
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