o-concurrent-ruby 1.1.11

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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +542 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +404 -0
  6. data/Rakefile +307 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby/concurrent/agent.rb +587 -0
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/concurrent-ruby/concurrent/async.rb +449 -0
  25. data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
  26. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  27. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  30. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  31. data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
  33. data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
  34. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
  35. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
  36. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  37. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  38. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  39. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
  40. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
  41. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
  42. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
  43. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
  44. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
  45. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  46. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  47. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  48. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  49. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  50. data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  51. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  52. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  53. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
  54. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  55. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  56. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  57. data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  58. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
  59. data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
  60. data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
  61. data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
  62. data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
  63. data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
  64. data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
  65. data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
  66. data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
  67. data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
  68. data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
  69. data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
  70. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
  71. data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
  72. data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
  73. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
  74. data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
  75. data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
  76. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
  77. data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
  78. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
  79. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
  80. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
  81. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
  82. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
  83. data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
  84. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
  85. data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
  86. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
  87. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
  88. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
  89. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
  90. data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
  91. data/lib/concurrent-ruby/concurrent/future.rb +141 -0
  92. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  93. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
  94. data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
  95. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  96. data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
  97. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
  98. data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
  99. data/lib/concurrent-ruby/concurrent/options.rb +42 -0
  100. data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
  101. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  102. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  103. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
  104. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  105. data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
  106. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  107. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
  108. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
  109. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
  110. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  111. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
  112. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
  113. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
  114. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
  115. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  116. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
  117. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
  118. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
  119. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  120. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
  121. data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
  122. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  123. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
  124. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  125. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  126. data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  127. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
  129. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  130. data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
  131. data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
  132. data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
  133. data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
  134. data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
  135. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  136. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  137. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
  138. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
  139. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  140. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  141. data/lib/concurrent-ruby/concurrent.rb +134 -0
  142. metadata +192 -0
@@ -0,0 +1,311 @@
1
+ require 'concurrent/collection/copy_on_notify_observer_set'
2
+ require 'concurrent/concern/dereferenceable'
3
+ require 'concurrent/concern/observable'
4
+ require 'concurrent/atomic/atomic_boolean'
5
+ require 'concurrent/executor/executor_service'
6
+ require 'concurrent/executor/ruby_executor_service'
7
+ require 'concurrent/executor/safe_task_executor'
8
+ require 'concurrent/scheduled_task'
9
+
10
+ module Concurrent
11
+
12
+ # A very common concurrency pattern is to run a thread that performs a task at
13
+ # regular intervals. The thread that performs the task sleeps for the given
14
+ # interval then wakes up and performs the task. Lather, rinse, repeat... This
15
+ # pattern causes two problems. First, it is difficult to test the business
16
+ # logic of the task because the task itself is tightly coupled with the
17
+ # concurrency logic. Second, an exception raised while performing the task can
18
+ # cause the entire thread to abend. In a long-running application where the
19
+ # task thread is intended to run for days/weeks/years a crashed task thread
20
+ # can pose a significant problem. `TimerTask` alleviates both problems.
21
+ #
22
+ # When a `TimerTask` is launched it starts a thread for monitoring the
23
+ # execution interval. The `TimerTask` thread does not perform the task,
24
+ # however. Instead, the TimerTask launches the task on a separate thread.
25
+ # Should the task experience an unrecoverable crash only the task thread will
26
+ # crash. This makes the `TimerTask` very fault tolerant. Additionally, the
27
+ # `TimerTask` thread can respond to the success or failure of the task,
28
+ # performing logging or ancillary operations.
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
+ # Dereferencing options can be passed to the `TimerTask` during construction or
43
+ # at any later time using the `#set_deref_options` method.
44
+ #
45
+ # `TimerTask` supports notification through the Ruby standard library
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).
50
+ #
51
+ # @!macro copy_options
52
+ #
53
+ # @example Basic usage
54
+ # task = Concurrent::TimerTask.new{ puts 'Boom!' }
55
+ # task.execute
56
+ #
57
+ # task.execution_interval #=> 60 (default)
58
+ #
59
+ # # wait 60 seconds...
60
+ # #=> 'Boom!'
61
+ #
62
+ # task.shutdown #=> true
63
+ #
64
+ # @example Configuring `:execution_interval`
65
+ # task = Concurrent::TimerTask.new(execution_interval: 5) do
66
+ # puts 'Boom!'
67
+ # end
68
+ #
69
+ # task.execution_interval #=> 5
70
+ #
71
+ # @example Immediate execution with `:run_now`
72
+ # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' }
73
+ # task.execute
74
+ #
75
+ # #=> 'Boom!'
76
+ #
77
+ # @example Last `#value` and `Dereferenceable` mixin
78
+ # task = Concurrent::TimerTask.new(
79
+ # dup_on_deref: true,
80
+ # execution_interval: 5
81
+ # ){ Time.now }
82
+ #
83
+ # task.execute
84
+ # Time.now #=> 2013-11-07 18:06:50 -0500
85
+ # sleep(10)
86
+ # task.value #=> 2013-11-07 18:06:55 -0500
87
+ #
88
+ # @example Controlling execution from within the block
89
+ # timer_task = Concurrent::TimerTask.new(execution_interval: 1) do |task|
90
+ # task.execution_interval.times{ print 'Boom! ' }
91
+ # print "\n"
92
+ # task.execution_interval += 1
93
+ # if task.execution_interval > 5
94
+ # puts 'Stopping...'
95
+ # task.shutdown
96
+ # end
97
+ # end
98
+ #
99
+ # timer_task.execute # blocking call - this task will stop itself
100
+ # #=> Boom!
101
+ # #=> Boom! Boom!
102
+ # #=> Boom! Boom! Boom!
103
+ # #=> Boom! Boom! Boom! Boom!
104
+ # #=> Boom! Boom! Boom! Boom! Boom!
105
+ # #=> Stopping...
106
+ #
107
+ # @example Observation
108
+ # class TaskObserver
109
+ # def update(time, result, ex)
110
+ # if result
111
+ # print "(#{time}) Execution successfully returned #{result}\n"
112
+ # else
113
+ # print "(#{time}) Execution failed with error #{ex}\n"
114
+ # end
115
+ # end
116
+ # end
117
+ #
118
+ # task = Concurrent::TimerTask.new(execution_interval: 1){ 42 }
119
+ # task.add_observer(TaskObserver.new)
120
+ # task.execute
121
+ # sleep 4
122
+ #
123
+ # #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42
124
+ # #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42
125
+ # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42
126
+ # task.shutdown
127
+ #
128
+ # task = Concurrent::TimerTask.new(execution_interval: 1){ sleep }
129
+ # task.add_observer(TaskObserver.new)
130
+ # task.execute
131
+ #
132
+ # #=> (2013-10-13 19:07:25 -0400) Execution timed out
133
+ # #=> (2013-10-13 19:07:27 -0400) Execution timed out
134
+ # #=> (2013-10-13 19:07:29 -0400) Execution timed out
135
+ # task.shutdown
136
+ #
137
+ # task = Concurrent::TimerTask.new(execution_interval: 1){ raise StandardError }
138
+ # task.add_observer(TaskObserver.new)
139
+ # task.execute
140
+ #
141
+ # #=> (2013-10-13 19:09:37 -0400) Execution failed with error StandardError
142
+ # #=> (2013-10-13 19:09:38 -0400) Execution failed with error StandardError
143
+ # #=> (2013-10-13 19:09:39 -0400) Execution failed with error StandardError
144
+ # task.shutdown
145
+ #
146
+ # @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
147
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html
148
+ class TimerTask < RubyExecutorService
149
+ include Concern::Dereferenceable
150
+ include Concern::Observable
151
+
152
+ # Default `:execution_interval` in seconds.
153
+ EXECUTION_INTERVAL = 60
154
+
155
+ # Default `:timeout_interval` in seconds.
156
+ TIMEOUT_INTERVAL = 30
157
+
158
+ # Create a new TimerTask with the given task and configuration.
159
+ #
160
+ # @!macro timer_task_initialize
161
+ # @param [Hash] opts the options defining task execution.
162
+ # @option opts [Integer] :execution_interval number of seconds between
163
+ # task executions (default: EXECUTION_INTERVAL)
164
+ # @option opts [Boolean] :run_now Whether to run the task immediately
165
+ # upon instantiation or to wait until the first # execution_interval
166
+ # has passed (default: false)
167
+ #
168
+ # @!macro deref_options
169
+ #
170
+ # @raise ArgumentError when no block is given.
171
+ #
172
+ # @yield to the block after :execution_interval seconds have passed since
173
+ # the last yield
174
+ # @yieldparam task a reference to the `TimerTask` instance so that the
175
+ # block can control its own lifecycle. Necessary since `self` will
176
+ # refer to the execution context of the block rather than the running
177
+ # `TimerTask`.
178
+ #
179
+ # @return [TimerTask] the new `TimerTask`
180
+ def initialize(opts = {}, &task)
181
+ raise ArgumentError.new('no block given') unless block_given?
182
+ super
183
+ set_deref_options opts
184
+ end
185
+
186
+ # Is the executor running?
187
+ #
188
+ # @return [Boolean] `true` when running, `false` when shutting down or shutdown
189
+ def running?
190
+ @running.true?
191
+ end
192
+
193
+ # Execute a previously created `TimerTask`.
194
+ #
195
+ # @return [TimerTask] a reference to `self`
196
+ #
197
+ # @example Instance and execute in separate steps
198
+ # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }
199
+ # task.running? #=> false
200
+ # task.execute
201
+ # task.running? #=> true
202
+ #
203
+ # @example Instance and execute in one line
204
+ # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute
205
+ # task.running? #=> true
206
+ def execute
207
+ synchronize do
208
+ if @running.false?
209
+ @running.make_true
210
+ schedule_next_task(@run_now ? 0 : @execution_interval)
211
+ end
212
+ end
213
+ self
214
+ end
215
+
216
+ # Create and execute a new `TimerTask`.
217
+ #
218
+ # @!macro timer_task_initialize
219
+ #
220
+ # @example
221
+ # task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" }
222
+ # task.running? #=> true
223
+ def self.execute(opts = {}, &task)
224
+ TimerTask.new(opts, &task).execute
225
+ end
226
+
227
+ # @!attribute [rw] execution_interval
228
+ # @return [Fixnum] Number of seconds after the task completes before the
229
+ # task is performed again.
230
+ def execution_interval
231
+ synchronize { @execution_interval }
232
+ end
233
+
234
+ # @!attribute [rw] execution_interval
235
+ # @return [Fixnum] Number of seconds after the task completes before the
236
+ # task is performed again.
237
+ def execution_interval=(value)
238
+ if (value = value.to_f) <= 0.0
239
+ raise ArgumentError.new('must be greater than zero')
240
+ else
241
+ synchronize { @execution_interval = value }
242
+ end
243
+ end
244
+
245
+ # @!attribute [rw] timeout_interval
246
+ # @return [Fixnum] Number of seconds the task can run before it is
247
+ # considered to have failed.
248
+ def timeout_interval
249
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
250
+ end
251
+
252
+ # @!attribute [rw] timeout_interval
253
+ # @return [Fixnum] Number of seconds the task can run before it is
254
+ # considered to have failed.
255
+ def timeout_interval=(value)
256
+ warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
257
+ end
258
+
259
+ private :post, :<<
260
+
261
+ private
262
+
263
+ def ns_initialize(opts, &task)
264
+ set_deref_options(opts)
265
+
266
+ self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
267
+ if opts[:timeout] || opts[:timeout_interval]
268
+ warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly'
269
+ end
270
+ @run_now = opts[:now] || opts[:run_now]
271
+ @executor = Concurrent::SafeTaskExecutor.new(task)
272
+ @running = Concurrent::AtomicBoolean.new(false)
273
+ @value = nil
274
+
275
+ self.observers = Collection::CopyOnNotifyObserverSet.new
276
+ end
277
+
278
+ # @!visibility private
279
+ def ns_shutdown_execution
280
+ @running.make_false
281
+ super
282
+ end
283
+
284
+ # @!visibility private
285
+ def ns_kill_execution
286
+ @running.make_false
287
+ super
288
+ end
289
+
290
+ # @!visibility private
291
+ def schedule_next_task(interval = execution_interval)
292
+ ScheduledTask.execute(interval, args: [Concurrent::Event.new], &method(:execute_task))
293
+ nil
294
+ end
295
+
296
+ # @!visibility private
297
+ def execute_task(completion)
298
+ return nil unless @running.true?
299
+ _success, value, reason = @executor.execute(self)
300
+ if completion.try?
301
+ self.value = value
302
+ schedule_next_task
303
+ time = Time.now
304
+ observers.notify_observers do
305
+ [time, self.value, reason]
306
+ end
307
+ end
308
+ nil
309
+ end
310
+ end
311
+ end
@@ -0,0 +1,86 @@
1
+ require 'concurrent/atomic/atomic_reference'
2
+
3
+ module Concurrent
4
+
5
+ # A fixed size array with volatile (synchronized, thread safe) getters/setters.
6
+ # Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal.
7
+ #
8
+ # @example
9
+ # tuple = Concurrent::Tuple.new(16)
10
+ #
11
+ # tuple.set(0, :foo) #=> :foo | volatile write
12
+ # tuple.get(0) #=> :foo | volatile read
13
+ # tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS
14
+ # tuple.cas(0, :foo, :baz) #=> false | strong CAS
15
+ # tuple.get(0) #=> :bar | volatile read
16
+ #
17
+ # @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia
18
+ # @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple
19
+ # @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable
20
+ class Tuple
21
+ include Enumerable
22
+
23
+ # The (fixed) size of the tuple.
24
+ attr_reader :size
25
+
26
+ # @!visibility private
27
+ Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
28
+ private_constant :Tuple
29
+
30
+ # Create a new tuple of the given size.
31
+ #
32
+ # @param [Integer] size the number of elements in the tuple
33
+ def initialize(size)
34
+ @size = size
35
+ @tuple = tuple = Tuple.new(size)
36
+ i = 0
37
+ while i < size
38
+ tuple[i] = Concurrent::AtomicReference.new
39
+ i += 1
40
+ end
41
+ end
42
+
43
+ # Get the value of the element at the given index.
44
+ #
45
+ # @param [Integer] i the index from which to retrieve the value
46
+ # @return [Object] the value at the given index or nil if the index is out of bounds
47
+ def get(i)
48
+ return nil if i >= @size || i < 0
49
+ @tuple[i].get
50
+ end
51
+ alias_method :volatile_get, :get
52
+
53
+ # Set the element at the given index to the given value
54
+ #
55
+ # @param [Integer] i the index for the element to set
56
+ # @param [Object] value the value to set at the given index
57
+ #
58
+ # @return [Object] the new value of the element at the given index or nil if the index is out of bounds
59
+ def set(i, value)
60
+ return nil if i >= @size || i < 0
61
+ @tuple[i].set(value)
62
+ end
63
+ alias_method :volatile_set, :set
64
+
65
+ # Set the value at the given index to the new value if and only if the current
66
+ # value matches the given old value.
67
+ #
68
+ # @param [Integer] i the index for the element to set
69
+ # @param [Object] old_value the value to compare against the current value
70
+ # @param [Object] new_value the value to set at the given index
71
+ #
72
+ # @return [Boolean] true if the value at the given element was set else false
73
+ def compare_and_set(i, old_value, new_value)
74
+ return false if i >= @size || i < 0
75
+ @tuple[i].compare_and_set(old_value, new_value)
76
+ end
77
+ alias_method :cas, :compare_and_set
78
+
79
+ # Calls the given block once for each element in self, passing that element as a parameter.
80
+ #
81
+ # @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index
82
+ def each
83
+ @tuple.each {|ref| yield ref.get}
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,221 @@
1
+ require 'set'
2
+ require 'concurrent/synchronization'
3
+
4
+ module Concurrent
5
+
6
+ # A `TVar` is a transactional variable - a single-element container that
7
+ # is used as part of a transaction - see `Concurrent::atomically`.
8
+ #
9
+ # @!macro thread_safe_variable_comparison
10
+ #
11
+ # {include:file:docs-source/tvar.md}
12
+ class TVar < Synchronization::Object
13
+ safe_initialization!
14
+
15
+ # Create a new `TVar` with an initial value.
16
+ def initialize(value)
17
+ @value = value
18
+ @lock = Mutex.new
19
+ end
20
+
21
+ # Get the value of a `TVar`.
22
+ def value
23
+ Concurrent::atomically do
24
+ Transaction::current.read(self)
25
+ end
26
+ end
27
+
28
+ # Set the value of a `TVar`.
29
+ def value=(value)
30
+ Concurrent::atomically do
31
+ Transaction::current.write(self, value)
32
+ end
33
+ end
34
+
35
+ # @!visibility private
36
+ def unsafe_value # :nodoc:
37
+ @value
38
+ end
39
+
40
+ # @!visibility private
41
+ def unsafe_value=(value) # :nodoc:
42
+ @value = value
43
+ end
44
+
45
+ # @!visibility private
46
+ def unsafe_lock # :nodoc:
47
+ @lock
48
+ end
49
+
50
+ end
51
+
52
+ # Run a block that reads and writes `TVar`s as a single atomic transaction.
53
+ # With respect to the value of `TVar` objects, the transaction is atomic, in
54
+ # that it either happens or it does not, consistent, in that the `TVar`
55
+ # objects involved will never enter an illegal state, and isolated, in that
56
+ # transactions never interfere with each other. You may recognise these
57
+ # properties from database transactions.
58
+ #
59
+ # There are some very important and unusual semantics that you must be aware of:
60
+ #
61
+ # * Most importantly, the block that you pass to atomically may be executed
62
+ # more than once. In most cases your code should be free of
63
+ # side-effects, except for via TVar.
64
+ #
65
+ # * If an exception escapes an atomically block it will abort the transaction.
66
+ #
67
+ # * It is undefined behaviour to use callcc or Fiber with atomically.
68
+ #
69
+ # * If you create a new thread within an atomically, it will not be part of
70
+ # the transaction. Creating a thread counts as a side-effect.
71
+ #
72
+ # Transactions within transactions are flattened to a single transaction.
73
+ #
74
+ # @example
75
+ # a = new TVar(100_000)
76
+ # b = new TVar(100)
77
+ #
78
+ # Concurrent::atomically do
79
+ # a.value -= 10
80
+ # b.value += 10
81
+ # end
82
+ def atomically
83
+ raise ArgumentError.new('no block given') unless block_given?
84
+
85
+ # Get the current transaction
86
+
87
+ transaction = Transaction::current
88
+
89
+ # Are we not already in a transaction (not nested)?
90
+
91
+ if transaction.nil?
92
+ # New transaction
93
+
94
+ begin
95
+ # Retry loop
96
+
97
+ loop do
98
+
99
+ # Create a new transaction
100
+
101
+ transaction = Transaction.new
102
+ Transaction::current = transaction
103
+
104
+ # Run the block, aborting on exceptions
105
+
106
+ begin
107
+ result = yield
108
+ rescue Transaction::AbortError => e
109
+ transaction.abort
110
+ result = Transaction::ABORTED
111
+ rescue Transaction::LeaveError => e
112
+ transaction.abort
113
+ break result
114
+ rescue => e
115
+ transaction.abort
116
+ raise e
117
+ end
118
+ # If we can commit, break out of the loop
119
+
120
+ if result != Transaction::ABORTED
121
+ if transaction.commit
122
+ break result
123
+ end
124
+ end
125
+ end
126
+ ensure
127
+ # Clear the current transaction
128
+
129
+ Transaction::current = nil
130
+ end
131
+ else
132
+ # Nested transaction - flatten it and just run the block
133
+
134
+ yield
135
+ end
136
+ end
137
+
138
+ # Abort a currently running transaction - see `Concurrent::atomically`.
139
+ def abort_transaction
140
+ raise Transaction::AbortError.new
141
+ end
142
+
143
+ # Leave a transaction without committing or aborting - see `Concurrent::atomically`.
144
+ def leave_transaction
145
+ raise Transaction::LeaveError.new
146
+ end
147
+
148
+ module_function :atomically, :abort_transaction, :leave_transaction
149
+
150
+ private
151
+
152
+ class Transaction
153
+
154
+ ABORTED = ::Object.new
155
+
156
+ OpenEntry = Struct.new(:value, :modified)
157
+
158
+ AbortError = Class.new(StandardError)
159
+ LeaveError = Class.new(StandardError)
160
+
161
+ def initialize
162
+ @open_tvars = {}
163
+ end
164
+
165
+ def read(tvar)
166
+ entry = open(tvar)
167
+ entry.value
168
+ end
169
+
170
+ def write(tvar, value)
171
+ entry = open(tvar)
172
+ entry.modified = true
173
+ entry.value = value
174
+ end
175
+
176
+ def open(tvar)
177
+ entry = @open_tvars[tvar]
178
+
179
+ unless entry
180
+ unless tvar.unsafe_lock.try_lock
181
+ Concurrent::abort_transaction
182
+ end
183
+
184
+ entry = OpenEntry.new(tvar.unsafe_value, false)
185
+ @open_tvars[tvar] = entry
186
+ end
187
+
188
+ entry
189
+ end
190
+
191
+ def abort
192
+ unlock
193
+ end
194
+
195
+ def commit
196
+ @open_tvars.each do |tvar, entry|
197
+ if entry.modified
198
+ tvar.unsafe_value = entry.value
199
+ end
200
+ end
201
+
202
+ unlock
203
+ end
204
+
205
+ def unlock
206
+ @open_tvars.each_key do |tvar|
207
+ tvar.unsafe_lock.unlock
208
+ end
209
+ end
210
+
211
+ def self.current
212
+ Thread.current[:current_tvar_transaction]
213
+ end
214
+
215
+ def self.current=(transaction)
216
+ Thread.current[:current_tvar_transaction] = transaction
217
+ end
218
+
219
+ end
220
+
221
+ end
@@ -0,0 +1,56 @@
1
+ module Concurrent
2
+ module Utility
3
+
4
+ # @!visibility private
5
+ module EngineDetector
6
+ def on_jruby?
7
+ ruby_engine == 'jruby'
8
+ end
9
+
10
+ def on_jruby_9000?
11
+ on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0)
12
+ end
13
+
14
+ def on_cruby?
15
+ ruby_engine == 'ruby'
16
+ end
17
+
18
+ def on_rbx?
19
+ ruby_engine == 'rbx'
20
+ end
21
+
22
+ def on_truffleruby?
23
+ ruby_engine == 'truffleruby'
24
+ end
25
+
26
+ def on_windows?
27
+ !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil?
28
+ end
29
+
30
+ def on_osx?
31
+ !(RbConfig::CONFIG['host_os'] =~ /darwin|mac os/).nil?
32
+ end
33
+
34
+ def on_linux?
35
+ !(RbConfig::CONFIG['host_os'] =~ /linux/).nil?
36
+ end
37
+
38
+ def ruby_engine
39
+ defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
40
+ end
41
+
42
+ def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch)
43
+ result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
44
+ comparisons = { :== => [0],
45
+ :>= => [1, 0],
46
+ :<= => [-1, 0],
47
+ :> => [1],
48
+ :< => [-1] }
49
+ comparisons.fetch(comparison).include? result
50
+ end
51
+ end
52
+ end
53
+
54
+ # @!visibility private
55
+ extend Utility::EngineDetector
56
+ end