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

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.
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