concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -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 +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  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 +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -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 +234 -109
  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 +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -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 +196 -23
  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_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -1,79 +1,377 @@
1
+ require 'concurrent/errors'
1
2
  require 'concurrent/ivar'
2
- require 'concurrent/utility/timer'
3
- require 'concurrent/executor/safe_task_executor'
3
+ require 'concurrent/configuration'
4
+ require 'concurrent/collection/copy_on_notify_observer_set'
5
+ require 'concurrent/executor/executor'
6
+ require 'concurrent/executor/timer_set'
7
+ require 'concurrent/utility/monotonic_time'
8
+ require 'concurrent/concern/deprecation'
4
9
 
5
10
  module Concurrent
11
+ include Concern::Deprecation
6
12
 
7
- # {include:file:doc/scheduled_task.md}
13
+ # `ScheduledTask` is a close relative of `Concurrent::Future` but with one
14
+ # important difference: A `Future` is set to execute as soon as possible
15
+ # whereas a `ScheduledTask` is set to execute after a specified delay. This
16
+ # implementation is loosely based on Java's
17
+ # [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html).
18
+ # It is a more feature-rich variant of {Concurrent.timer}.
19
+ #
20
+ # The *intended* schedule time of task execution is set on object construction
21
+ # with the `delay` argument. The delay is a numeric (floating point or integer)
22
+ # representing a number of seconds in the future. Any other value or a numeric
23
+ # equal to or less than zero will result in an exception. The *actual* schedule
24
+ # time of task execution is set when the `execute` method is called.
25
+ #
26
+ # The constructor can also be given zero or more processing options. Currently
27
+ # the only supported options are those recognized by the
28
+ # [Dereferenceable](Dereferenceable) module.
29
+ #
30
+ # The final constructor argument is a block representing the task to be performed.
31
+ # If no block is given an `ArgumentError` will be raised.
32
+ #
33
+ # **States**
34
+ #
35
+ # `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it
36
+ # "future" behavior. This includes the expected lifecycle states. `ScheduledTask`
37
+ # has one additional state, however. While the task (block) is being executed the
38
+ # state of the object will be `:processing`. This additional state is necessary
39
+ # because it has implications for task cancellation.
40
+ #
41
+ # **Cancellation**
42
+ #
43
+ # A `:pending` task can be cancelled using the `#cancel` method. A task in any
44
+ # other state, including `:processing`, cannot be cancelled. The `#cancel`
45
+ # method returns a boolean indicating the success of the cancellation attempt.
46
+ # A cancelled `ScheduledTask` cannot be restarted. It is immutable.
47
+ #
48
+ # **Obligation and Observation**
49
+ #
50
+ # The result of a `ScheduledTask` can be obtained either synchronously or
51
+ # asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation)
52
+ # module and the
53
+ # [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
54
+ # module from the Ruby standard library. With one exception `ScheduledTask`
55
+ # behaves identically to [Future](Observable) with regard to these modules.
56
+ #
57
+ # @!macro copy_options
58
+ #
59
+ # @example Basic usage
60
+ #
61
+ # require 'concurrent'
62
+ # require 'thread' # for Queue
63
+ # require 'open-uri' # for open(uri)
64
+ #
65
+ # class Ticker
66
+ # def get_year_end_closing(symbol, year)
67
+ # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
68
+ # data = open(uri) {|f| f.collect{|line| line.strip } }
69
+ # data[1].split(',')[4].to_f
70
+ # end
71
+ # end
72
+ #
73
+ # # Future
74
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
75
+ # price.state #=> :pending
76
+ # sleep(1) # do other stuff
77
+ # price.value #=> 63.65
78
+ # price.state #=> :fulfilled
79
+ #
80
+ # # ScheduledTask
81
+ # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
82
+ # task.state #=> :pending
83
+ # sleep(3) # do other stuff
84
+ # task.value #=> 25.96
85
+ #
86
+ # @example Successful task execution
87
+ #
88
+ # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
89
+ # task.state #=> :unscheduled
90
+ # task.execute
91
+ # task.state #=> pending
92
+ #
93
+ # # wait for it...
94
+ # sleep(3)
95
+ #
96
+ # task.unscheduled? #=> false
97
+ # task.pending? #=> false
98
+ # task.fulfilled? #=> true
99
+ # task.rejected? #=> false
100
+ # task.value #=> 'What does the fox say?'
101
+ #
102
+ # @example One line creation and execution
103
+ #
104
+ # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute
105
+ # task.state #=> pending
106
+ #
107
+ # task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' }
108
+ # task.state #=> pending
109
+ #
110
+ # @example Failed task execution
111
+ #
112
+ # task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') }
113
+ # task.pending? #=> true
114
+ #
115
+ # # wait for it...
116
+ # sleep(3)
117
+ #
118
+ # task.unscheduled? #=> false
119
+ # task.pending? #=> false
120
+ # task.fulfilled? #=> false
121
+ # task.rejected? #=> true
122
+ # task.value #=> nil
123
+ # task.reason #=> #<StandardError: Call me maybe?>
124
+ #
125
+ # @example Task execution with observation
126
+ #
127
+ # observer = Class.new{
128
+ # def update(time, value, reason)
129
+ # puts "The task completed at #{time} with value '#{value}'"
130
+ # end
131
+ # }.new
132
+ #
133
+ # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
134
+ # task.add_observer(observer)
135
+ # task.execute
136
+ # task.pending? #=> true
137
+ #
138
+ # # wait for it...
139
+ # sleep(3)
140
+ #
141
+ # #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?'
142
+ #
143
+ # @!macro monotonic_clock_warning
144
+ #
145
+ # @see Concurrent.timer
8
146
  class ScheduledTask < IVar
147
+ include Comparable
9
148
 
10
- attr_reader :schedule_time
149
+ # The executor on which to execute the task.
150
+ # @!visibility private
151
+ attr_reader :executor
11
152
 
12
- def initialize(intended_time, opts = {}, &block)
153
+ # Schedule a task for execution at a specified future time.
154
+ #
155
+ # @param [Float] delay the number of seconds to wait for before executing the task
156
+ #
157
+ # @yield the task to be performed
158
+ #
159
+ # @!macro executor_and_deref_options
160
+ #
161
+ # @option opts [object, Array] :args zero or more arguments to be passed the task
162
+ # block on execution
163
+ #
164
+ # @raise [ArgumentError] When no block is given
165
+ # @raise [ArgumentError] When given a time that is in the past
166
+ #
167
+ # @!macro [attach] deprecated_scheduling_by_clock_time
168
+ #
169
+ # @note Scheduling is now based on a monotonic clock. This makes the timer much
170
+ # more accurate, but only when scheduling based on a delay interval.
171
+ # Scheduling a task based on a clock time is deprecated. It will still work
172
+ # but will not be supported in the 1.0 release.
173
+ def initialize(delay, opts = {}, &task)
13
174
  raise ArgumentError.new('no block given') unless block_given?
14
- TimerSet.calculate_schedule_time(intended_time) # raises exceptons
175
+ super(IVar::NO_VALUE, opts, &nil)
176
+ synchronize do
177
+ @delay = calculate_delay!(delay) # may raise exception
178
+ ns_set_state(:unscheduled)
179
+ @parent = opts.fetch(:timer_set, Concurrent.global_timer_set)
180
+ @args = get_arguments_from(opts)
181
+ @task = task
182
+ @time = nil
183
+ @executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
184
+ self.observers = Collection::CopyOnNotifyObserverSet.new
185
+ end
186
+ end
15
187
 
16
- super(NO_VALUE, opts)
188
+ # The `delay` value given at instanciation.
189
+ #
190
+ # @return [Float] the initial delay.
191
+ def initial_delay
192
+ synchronize { @delay }
193
+ end
17
194
 
18
- self.observers = CopyOnNotifyObserverSet.new
19
- @intended_time = intended_time
20
- @state = :unscheduled
21
- @task = block
22
- @executor = OptionsParser::get_executor_from(opts) || Concurrent.configuration.global_operation_pool
195
+ # The `delay` value given at instanciation.
196
+ #
197
+ # @return [Float] the initial delay.
198
+ #
199
+ # @deprecated use {#initial_delay} instead
200
+ def delay
201
+ deprecated_method 'delay', 'initial_delay'
202
+ initial_delay
23
203
  end
24
204
 
25
- # @since 0.5.0
26
- def execute
27
- if compare_and_set_state(:pending, :unscheduled)
28
- @schedule_time = TimerSet.calculate_schedule_time(@intended_time)
29
- Concurrent::timer(@schedule_time.to_f - Time.now.to_f) { @executor.post(&method(:process_task)) }
30
- self
31
- end
205
+ # The monotonic time at which the the task is scheduled to be executed.
206
+ #
207
+ # @return [Float] the schedule time or nil if `unscheduled`
208
+ def schedule_time
209
+ synchronize { @time }
32
210
  end
33
211
 
34
- # @since 0.5.0
35
- def self.execute(intended_time, opts = {}, &block)
36
- return ScheduledTask.new(intended_time, opts, &block).execute
212
+ # Comparator which orders by schedule time.
213
+ #
214
+ # @!visibility private
215
+ def <=>(other)
216
+ schedule_time <=> other.schedule_time
37
217
  end
38
218
 
219
+ # Has the task been cancelled?
220
+ #
221
+ # @return [Boolean] true if the task is in the given state else false
39
222
  def cancelled?
40
- state == :cancelled
223
+ synchronize { ns_check_state?(:cancelled) }
41
224
  end
42
225
 
226
+ # In the task execution in progress?
227
+ #
228
+ # @return [Boolean] true if the task is in the given state else false
229
+ def processing?
230
+ synchronize { ns_check_state?(:processing) }
231
+ end
232
+
233
+ # In the task execution in progress?
234
+ #
235
+ # @return [Boolean] true if the task is in the given state else false
236
+ #
237
+ # @deprecated Use {#processing?} instead.
43
238
  def in_progress?
44
- state == :in_progress
239
+ deprecated_method 'in_progress?', 'processing?'
240
+ processing?
45
241
  end
46
242
 
243
+ # Cancel this task and prevent it from executing. A task can only be
244
+ # cancelled if it is pending or unscheduled.
245
+ #
246
+ # @return [Boolean] true if successfully cancelled else false
47
247
  def cancel
48
- if_state(:unscheduled, :pending) do
49
- @state = :cancelled
50
- event.set
51
- true
248
+ if compare_and_set_state(:cancelled, :pending, :unscheduled)
249
+ complete(false, nil, CancelledOperationError.new)
250
+ # To avoid deadlocks this call must occur outside of #synchronize
251
+ # Changing the state above should prevent redundant calls
252
+ @parent.send(:remove_task, self)
253
+ else
254
+ false
52
255
  end
53
256
  end
54
- alias_method :stop, :cancel
55
257
 
56
- def add_observer(*args, &block)
57
- if_state(:unscheduled, :pending, :in_progress) do
58
- observers.add_observer(*args, &block)
59
- end
258
+ # Cancel this task and prevent it from executing. A task can only be
259
+ # cancelled if it is `:pending` or `:unscheduled`.
260
+ #
261
+ # @return [Boolean] true if successfully cancelled else false
262
+ #
263
+ # @deprecated Use {#cancel} instead.
264
+ def stop
265
+ deprecated_method 'stop', 'cancel'
266
+ cancel
267
+ end
268
+
269
+ # Reschedule the task using the original delay and the current time.
270
+ # A task can only be reset while it is `:pending`.
271
+ #
272
+ # @return [Boolean] true if successfully rescheduled else false
273
+ def reset
274
+ synchronize{ ns_reschedule(@delay) }
275
+ end
276
+
277
+ # Reschedule the task using the given delay and the current time.
278
+ # A task can only be reset while it is `:pending`.
279
+ #
280
+ # @param [Float] delay the number of seconds to wait for before executing the task
281
+ #
282
+ # @return [Boolean] true if successfully rescheduled else false
283
+ #
284
+ # @raise [ArgumentError] When given a time that is in the past
285
+ def reschedule(delay)
286
+ synchronize{ ns_reschedule(calculate_delay!(delay)) }
60
287
  end
61
288
 
62
- protected :set, :fail, :complete
289
+ # Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending`
290
+ # and starts counting down toward execution. Does nothing if the `ScheduledTask` is
291
+ # in any state other than `:unscheduled`.
292
+ #
293
+ # @return [ScheduledTask] a reference to `self`
294
+ def execute
295
+ if compare_and_set_state(:pending, :unscheduled)
296
+ synchronize{ ns_schedule(@delay) }
297
+ end
298
+ self
299
+ end
63
300
 
64
- private
301
+ # Create a new `ScheduledTask` object with the given block, execute it, and return the
302
+ # `:pending` object.
303
+ #
304
+ # @param [Float] delay the number of seconds to wait for before executing the task
305
+ #
306
+ # @!macro executor_and_deref_options
307
+ #
308
+ # @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state
309
+ #
310
+ # @raise [ArgumentError] if no block is given
311
+ #
312
+ # @!macro deprecated_scheduling_by_clock_time
313
+ def self.execute(delay, opts = {}, &task)
314
+ new(delay, opts, &task).execute
315
+ end
65
316
 
317
+ # Execute the task.
318
+ #
319
+ # @!visibility private
66
320
  def process_task
67
- if compare_and_set_state(:in_progress, :pending)
68
- success, val, reason = SafeTaskExecutor.new(@task).execute
321
+ safe_execute(@task, @args)
322
+ end
323
+
324
+ protected :set, :try_set, :fail, :complete
325
+
326
+ protected
69
327
 
70
- mutex.synchronize do
71
- set_state(success, val, reason)
72
- event.set
73
- end
328
+ # Schedule the task using the given delay and the current time.
329
+ #
330
+ # @param [Float] delay the number of seconds to wait for before executing the task
331
+ #
332
+ # @return [Boolean] true if successfully rescheduled else false
333
+ #
334
+ # @!visibility private
335
+ def ns_schedule(delay)
336
+ @delay = delay
337
+ @time = Concurrent.monotonic_time + @delay
338
+ @parent.send(:post_task, self)
339
+ end
340
+
341
+ # Reschedule the task using the given delay and the current time.
342
+ # A task can only be reset while it is `:pending`.
343
+ #
344
+ # @param [Float] delay the number of seconds to wait for before executing the task
345
+ #
346
+ # @return [Boolean] true if successfully rescheduled else false
347
+ #
348
+ # @!visibility private
349
+ def ns_reschedule(delay)
350
+ return false unless ns_check_state?(:pending)
351
+ @parent.send(:remove_task, self) && ns_schedule(delay)
352
+ end
74
353
 
75
- time = Time.now
76
- observers.notify_and_delete_observers { [time, self.value, reason] }
354
+ # Calculate the actual delay in seconds based on the given delay.
355
+ #
356
+ # @param [Float] delay the number of seconds to wait for before executing the task
357
+ #
358
+ # @return [Float] the number of seconds to delay
359
+ #
360
+ # @raise [ArgumentError] if the intended execution time is not in the future
361
+ # @raise [ArgumentError] if no block is given
362
+ #
363
+ # @!macro deprecated_scheduling_by_clock_time
364
+ #
365
+ # @!visibility private
366
+ def calculate_delay!(delay)
367
+ if delay.is_a?(Time)
368
+ deprecated 'Use an interval not a clock time; schedule is now based on a monotonic clock'
369
+ now = Time.now
370
+ raise ArgumentError.new('schedule time must be in the future') if delay <= now
371
+ delay.to_f - now.to_f
372
+ else
373
+ raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0
374
+ delay.to_f
77
375
  end
78
376
  end
79
377
  end
@@ -0,0 +1,127 @@
1
+ require 'concurrent/synchronization/abstract_struct'
2
+ require 'concurrent/errors'
3
+ require 'concurrent/synchronization'
4
+
5
+ module Concurrent
6
+
7
+ # An thread-safe, write-once variation of Ruby's standard `Struct`.
8
+ # Each member can have its value set at most once, either at construction
9
+ # or any time thereafter. Attempting to assign a value to a member
10
+ # that has already been set will result in a `Concurrent::ImmutabilityError`.
11
+ #
12
+ # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
13
+ # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
14
+ module SettableStruct
15
+ include Synchronization::AbstractStruct
16
+
17
+ # @!macro struct_values
18
+ def values
19
+ synchronize { ns_values }
20
+ end
21
+ alias_method :to_a, :values
22
+
23
+ # @!macro struct_values_at
24
+ def values_at(*indexes)
25
+ synchronize { ns_values_at(indexes) }
26
+ end
27
+
28
+ # @!macro struct_inspect
29
+ def inspect
30
+ synchronize { ns_inspect }
31
+ end
32
+ alias_method :to_s, :inspect
33
+
34
+ # @!macro struct_merge
35
+ def merge(other, &block)
36
+ synchronize { ns_merge(other, &block) }
37
+ end
38
+
39
+ # @!macro struct_to_h
40
+ def to_h
41
+ synchronize { ns_to_h }
42
+ end
43
+
44
+ # @!macro struct_get
45
+ def [](member)
46
+ synchronize { ns_get(member) }
47
+ end
48
+
49
+ # @!macro struct_equality
50
+ def ==(other)
51
+ synchronize { ns_equality(other) }
52
+ end
53
+
54
+ # @!macro struct_each
55
+ def each(&block)
56
+ return enum_for(:each) unless block_given?
57
+ synchronize { ns_each(&block) }
58
+ end
59
+
60
+ # @!macro struct_each_pair
61
+ def each_pair(&block)
62
+ return enum_for(:each_pair) unless block_given?
63
+ synchronize { ns_each_pair(&block) }
64
+ end
65
+
66
+ # @!macro struct_select
67
+ def select(&block)
68
+ return enum_for(:select) unless block_given?
69
+ synchronize { ns_select(&block) }
70
+ end
71
+
72
+ # @!macro struct_set
73
+ #
74
+ # @raise [Concurrent::ImmutabilityError] if the given member has already been set
75
+ def []=(member, value)
76
+ if member.is_a? Integer
77
+ if member >= @values.length
78
+ raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
79
+ end
80
+ synchronize do
81
+ unless @values[member].nil?
82
+ raise Concurrent::ImmutabilityError.new('struct member has already been set')
83
+ end
84
+ @values[member] = value
85
+ end
86
+ else
87
+ send("#{member}=", value)
88
+ end
89
+ rescue NoMethodError
90
+ raise NameError.new("no member '#{member}' in struct")
91
+ end
92
+
93
+ # @!macro struct_new
94
+ def self.new(*args, &block)
95
+ clazz_name = nil
96
+ if args.length == 0
97
+ raise ArgumentError.new('wrong number of arguments (0 for 1+)')
98
+ elsif args.length > 0 && args.first.is_a?(String)
99
+ clazz_name = args.shift
100
+ end
101
+ FACTORY.define_struct(clazz_name, args, &block)
102
+ end
103
+
104
+ FACTORY = Class.new(Synchronization::Object) do
105
+ def define_struct(name, members, &block)
106
+ synchronize do
107
+ clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::Object, name, members, &block)
108
+ members.each_with_index do |member, index|
109
+ clazz.send(:define_method, member) do
110
+ synchronize { @values[index] }
111
+ end
112
+ clazz.send(:define_method, "#{member}=") do |value|
113
+ synchronize do
114
+ unless @values[index].nil?
115
+ raise Concurrent::ImmutabilityError.new('struct member has already been set')
116
+ end
117
+ @values[index] = value
118
+ end
119
+ end
120
+ end
121
+ clazz
122
+ end
123
+ end
124
+ end.new
125
+ private_constant :FACTORY
126
+ end
127
+ end