concurrent-ruby 1.0.5 → 1.1.6

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 (161) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +115 -0
  3. data/Gemfile +42 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +242 -105
  6. data/Rakefile +332 -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 +159 -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-ruby.rb +1 -0
  23. data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +7 -7
  25. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +18 -4
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
  28. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
  29. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
  31. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +1 -1
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
  39. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +0 -0
  42. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
  43. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +3 -1
  44. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +43 -33
  45. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +8 -8
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
  49. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
  51. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  53. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
  55. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +0 -0
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +1 -2
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +0 -0
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
  65. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +19 -25
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +27 -30
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  77. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +19 -16
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  80. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +12 -8
  81. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +0 -2
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +9 -4
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +0 -0
  85. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  86. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
  87. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  88. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
  89. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
  90. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +6 -6
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
  92. data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
  94. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +8 -0
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
  97. data/lib/concurrent-ruby/concurrent/map.rb +337 -0
  98. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +25 -14
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
  101. data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  102. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +53 -21
  103. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  105. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +0 -0
  106. data/lib/concurrent-ruby/concurrent/set.rb +66 -0
  107. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +11 -0
  108. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
  109. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
  110. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
  111. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
  112. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  113. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
  114. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
  115. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  116. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +6 -6
  117. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
  118. data/lib/{concurrent/synchronization/mri_lockable_object.rb → concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb} +19 -14
  119. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
  120. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +0 -0
  121. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
  122. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
  124. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
  125. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  126. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  127. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  130. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
  131. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  132. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  133. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +5 -2
  134. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
  135. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +2 -2
  136. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
  137. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/monotonic_time.rb +3 -3
  138. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  139. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
  140. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -2
  141. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  142. metadata +146 -131
  143. data/lib/concurrent/array.rb +0 -39
  144. data/lib/concurrent/atomic/atomic_reference.rb +0 -51
  145. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  146. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  147. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  148. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  149. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  150. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  151. data/lib/concurrent/atomics.rb +0 -53
  152. data/lib/concurrent/edge.rb +0 -26
  153. data/lib/concurrent/hash.rb +0 -36
  154. data/lib/concurrent/lazy_register.rb +0 -81
  155. data/lib/concurrent/map.rb +0 -240
  156. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  157. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  158. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
  159. data/lib/concurrent/utility/at_exit.rb +0 -97
  160. data/lib/concurrent/utility/native_extension_loader.rb +0 -73
  161. data/lib/concurrent/version.rb +0 -4
@@ -122,8 +122,6 @@ module Concurrent
122
122
  raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE
123
123
  raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length
124
124
 
125
- self.auto_terminate = opts.fetch(:auto_terminate, true)
126
-
127
125
  @pool = [] # all workers
128
126
  @ready = [] # used as a stash (most idle worker is at the start)
129
127
  @queue = [] # used as queue
@@ -131,6 +129,7 @@ module Concurrent
131
129
  @scheduled_task_count = 0
132
130
  @completed_task_count = 0
133
131
  @largest_length = 0
132
+ @workers_counter = 0
134
133
  @ruby_pid = $$ # detects if Ruby has forked
135
134
 
136
135
  @gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented
@@ -224,7 +223,8 @@ module Concurrent
224
223
  def ns_add_busy_worker
225
224
  return if @pool.size >= @max_length
226
225
 
227
- @pool << (worker = Worker.new(self))
226
+ @workers_counter += 1
227
+ @pool << (worker = Worker.new(self, @workers_counter))
228
228
  @largest_length = @pool.length if @pool.length > @largest_length
229
229
  worker
230
230
  end
@@ -284,6 +284,7 @@ module Concurrent
284
284
  @scheduled_task_count = 0
285
285
  @completed_task_count = 0
286
286
  @largest_length = 0
287
+ @workers_counter = 0
287
288
  @ruby_pid = $$
288
289
  end
289
290
  end
@@ -292,11 +293,15 @@ module Concurrent
292
293
  class Worker
293
294
  include Concern::Logging
294
295
 
295
- def initialize(pool)
296
+ def initialize(pool, id)
296
297
  # instance variables accessed only under pool's lock so no need to sync here again
297
298
  @queue = Queue.new
298
299
  @pool = pool
299
300
  @thread = create_worker @queue, pool, pool.idletime
301
+
302
+ if @thread.respond_to?(:name=)
303
+ @thread.name = [pool.name, 'worker', id].compact.join('-')
304
+ end
300
305
  end
301
306
 
302
307
  def <<(message)
@@ -91,7 +91,7 @@ module Concurrent
91
91
 
92
92
  private
93
93
 
94
- def ns_initialize
94
+ def ns_initialize(*args)
95
95
  @running = Concurrent::AtomicBoolean.new(true)
96
96
  @stopped = Concurrent::Event.new
97
97
  @count = Concurrent::AtomicFixnum.new(0)
@@ -1,3 +1,4 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/executor/ruby_single_thread_executor'
2
3
 
3
4
  module Concurrent
@@ -14,7 +15,7 @@ module Concurrent
14
15
  end
15
16
  private_constant :SingleThreadExecutorImplementation
16
17
 
17
- # @!macro [attach] single_thread_executor
18
+ # @!macro single_thread_executor
18
19
  #
19
20
  # A thread pool with a single thread an unlimited queue. Should the thread
20
21
  # die for any reason it will be removed and replaced, thus ensuring that
@@ -35,7 +36,7 @@ module Concurrent
35
36
  # @!macro abstract_executor_service_public_api
36
37
  class SingleThreadExecutor < SingleThreadExecutorImplementation
37
38
 
38
- # @!macro [new] single_thread_executor_method_initialize
39
+ # @!macro single_thread_executor_method_initialize
39
40
  #
40
41
  # Create a new thread pool.
41
42
  #
@@ -15,7 +15,7 @@ module Concurrent
15
15
  end
16
16
  private_constant :ThreadPoolExecutorImplementation
17
17
 
18
- # @!macro [attach] thread_pool_executor
18
+ # @!macro thread_pool_executor
19
19
  #
20
20
  # An abstraction composed of one or more threads and a task queue. Tasks
21
21
  # (blocks or `proc` objects) are submitted to the pool and added to the queue.
@@ -55,12 +55,12 @@ module Concurrent
55
55
  # @!macro thread_pool_executor_public_api
56
56
  class ThreadPoolExecutor < ThreadPoolExecutorImplementation
57
57
 
58
- # @!macro [new] thread_pool_executor_method_initialize
58
+ # @!macro thread_pool_executor_method_initialize
59
59
  #
60
60
  # Create a new thread pool.
61
- #
61
+ #
62
62
  # @param [Hash] opts the options which configure the thread pool.
63
- #
63
+ #
64
64
  # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
65
65
  # number of threads to be created
66
66
  # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted
@@ -73,12 +73,12 @@ module Concurrent
73
73
  # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
74
74
  # tasks that are received when the queue size has reached
75
75
  # `max_queue` or the executor has shut down
76
- #
76
+ #
77
77
  # @raise [ArgumentError] if `:max_threads` is less than one
78
78
  # @raise [ArgumentError] if `:min_threads` is less than zero
79
79
  # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
80
80
  # in `FALLBACK_POLICIES`
81
- #
81
+ #
82
82
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
83
83
 
84
84
  # @!method initialize(opts = {})
@@ -20,7 +20,7 @@ module Concurrent
20
20
 
21
21
  # Create a new set of timed tasks.
22
22
  #
23
- # @!macro [attach] executor_options
23
+ # @!macro executor_options
24
24
  #
25
25
  # @param [Hash] opts the options used to specify the executor on which to perform actions
26
26
  # @option opts [Executor] :executor when set use the given `Executor` instance.
@@ -48,11 +48,9 @@ module Concurrent
48
48
  def post(delay, *args, &task)
49
49
  raise ArgumentError.new('no block given') unless block_given?
50
50
  return false unless running?
51
- opts = {
52
- executor: @task_executor,
53
- args: args,
54
- timer_set: self
55
- }
51
+ opts = { executor: @task_executor,
52
+ args: args,
53
+ timer_set: self }
56
54
  task = ScheduledTask.execute(delay, opts, &task) # may raise exception
57
55
  task.unscheduled? ? false : task
58
56
  end
@@ -74,12 +72,11 @@ module Concurrent
74
72
  # @param [Hash] opts the options to create the object with.
75
73
  # @!visibility private
76
74
  def ns_initialize(opts)
77
- @queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
78
- @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
79
- @timer_executor = SingleThreadExecutor.new
80
- @condition = Event.new
81
- @ruby_pid = $$ # detects if Ruby has forked
82
- self.auto_terminate = opts.fetch(:auto_terminate, true)
75
+ @queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
76
+ @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
77
+ @timer_executor = SingleThreadExecutor.new
78
+ @condition = Event.new
79
+ @ruby_pid = $$ # detects if Ruby has forked
83
80
  end
84
81
 
85
82
  # Post the task to the internal queue.
@@ -90,7 +87,7 @@ module Concurrent
90
87
  #
91
88
  # @!visibility private
92
89
  def post_task(task)
93
- synchronize{ ns_post_task(task) }
90
+ synchronize { ns_post_task(task) }
94
91
  end
95
92
 
96
93
  # @!visibility private
@@ -98,7 +95,7 @@ module Concurrent
98
95
  return false unless ns_running?
99
96
  ns_reset_if_forked
100
97
  if (task.initial_delay) <= 0.01
101
- task.executor.post{ task.process_task }
98
+ task.executor.post { task.process_task }
102
99
  else
103
100
  @queue.push(task)
104
101
  # only post the process method when the queue is empty
@@ -116,7 +113,7 @@ module Concurrent
116
113
  #
117
114
  # @!visibility private
118
115
  def remove_task(task)
119
- synchronize{ @queue.delete(task) }
116
+ synchronize { @queue.delete(task) }
120
117
  end
121
118
 
122
119
  # `ExecutorService` callback called during shutdown.
@@ -148,7 +145,7 @@ module Concurrent
148
145
  task = synchronize { @condition.reset; @queue.peek }
149
146
  break unless task
150
147
 
151
- now = Concurrent.monotonic_time
148
+ now = Concurrent.monotonic_time
152
149
  diff = task.schedule_time - now
153
150
 
154
151
  if diff <= 0
@@ -165,7 +162,7 @@ module Concurrent
165
162
  # queue now must have the same pop time, or a closer one, as
166
163
  # when we peeked).
167
164
  task = synchronize { @queue.pop }
168
- task.executor.post{ task.process_task }
165
+ task.executor.post { task.process_task }
169
166
  else
170
167
  @condition.wait([diff, 60].min)
171
168
  end
@@ -6,9 +6,12 @@ require 'concurrent/executor/safe_task_executor'
6
6
 
7
7
  require 'concurrent/options'
8
8
 
9
+ # TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc.
10
+
11
+
9
12
  module Concurrent
10
13
 
11
- # {include:file:doc/future.md}
14
+ # {include:file:docs-source/future.md}
12
15
  #
13
16
  # @!macro copy_options
14
17
  #
@@ -0,0 +1,59 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/thread_safe/util'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro concurrent_hash
7
+ #
8
+ # A thread-safe subclass of Hash. This version locks against the object
9
+ # itself for every method call, ensuring only one thread can be reading
10
+ # or writing at a time. This includes iteration methods like `#each`,
11
+ # which takes the lock repeatedly when reading an item.
12
+ #
13
+ # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash`
14
+
15
+ # @!macro internal_implementation_note
16
+ HashImplementation = case
17
+ when Concurrent.on_cruby?
18
+ # Hash is thread-safe in practice because CRuby runs
19
+ # threads one at a time and does not do context
20
+ # switching during the execution of C functions.
21
+ ::Hash
22
+
23
+ when Concurrent.on_jruby?
24
+ require 'jruby/synchronized'
25
+
26
+ class JRubyHash < ::Hash
27
+ include JRuby::Synchronized
28
+ end
29
+ JRubyHash
30
+
31
+ when Concurrent.on_rbx?
32
+ require 'monitor'
33
+ require 'concurrent/thread_safe/util/data_structures'
34
+
35
+ class RbxHash < ::Hash
36
+ end
37
+ ThreadSafe::Util.make_synchronized_on_rbx RbxHash
38
+ RbxHash
39
+
40
+ when Concurrent.on_truffleruby?
41
+ require 'concurrent/thread_safe/util/data_structures'
42
+
43
+ class TruffleRubyHash < ::Hash
44
+ end
45
+
46
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash
47
+ TruffleRubyHash
48
+
49
+ else
50
+ warn 'Possibly unsupported Ruby implementation'
51
+ ::Hash
52
+ end
53
+ private_constant :HashImplementation
54
+
55
+ # @!macro concurrent_hash
56
+ class Hash < HashImplementation
57
+ end
58
+
59
+ end
@@ -70,6 +70,14 @@ module Concurrent
70
70
  ns_select(&block)
71
71
  end
72
72
 
73
+ private
74
+
75
+ # @!visibility private
76
+ def initialize_copy(original)
77
+ super(original)
78
+ ns_initialize_copy
79
+ end
80
+
73
81
  # @!macro struct_new
74
82
  def self.new(*args, &block)
75
83
  clazz_name = nil
@@ -98,10 +98,10 @@ module Concurrent
98
98
  observer
99
99
  end
100
100
 
101
- # @!macro [attach] ivar_set_method
101
+ # @!macro ivar_set_method
102
102
  # Set the `IVar` to a value and wake or notify all threads waiting on it.
103
103
  #
104
- # @!macro [attach] ivar_set_parameters_and_exceptions
104
+ # @!macro ivar_set_parameters_and_exceptions
105
105
  # @param [Object] value the value to store in the `IVar`
106
106
  # @yield A block operation to use for setting the value
107
107
  # @raise [ArgumentError] if both a value and a block are given
@@ -124,7 +124,7 @@ module Concurrent
124
124
  self
125
125
  end
126
126
 
127
- # @!macro [attach] ivar_fail_method
127
+ # @!macro ivar_fail_method
128
128
  # Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
129
129
  #
130
130
  # @param [Object] reason for the failure
@@ -157,9 +157,8 @@ module Concurrent
157
157
  self.observers = Collection::CopyOnWriteObserverSet.new
158
158
  set_deref_options(opts)
159
159
 
160
- if value == NULL
161
- @state = :pending
162
- else
160
+ @state = :pending
161
+ if value != NULL
163
162
  ns_complete_without_notification(true, value, nil)
164
163
  end
165
164
  end
@@ -0,0 +1,337 @@
1
+ require 'thread'
2
+ require 'concurrent/constants'
3
+ require 'concurrent/synchronization'
4
+ require 'concurrent/utility/engine'
5
+
6
+ module Concurrent
7
+ # @!visibility private
8
+ module Collection
9
+
10
+ # @!visibility private
11
+ MapImplementation = case
12
+ when Concurrent.on_jruby?
13
+ # noinspection RubyResolve
14
+ JRubyMapBackend
15
+ when Concurrent.on_cruby?
16
+ require 'concurrent/collection/map/mri_map_backend'
17
+ MriMapBackend
18
+ when Concurrent.on_rbx? || Concurrent.on_truffleruby?
19
+ require 'concurrent/collection/map/atomic_reference_map_backend'
20
+ AtomicReferenceMapBackend
21
+ else
22
+ warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
23
+ require 'concurrent/collection/map/synchronized_map_backend'
24
+ SynchronizedMapBackend
25
+ end
26
+ end
27
+
28
+ # `Concurrent::Map` is a hash-like object and should have much better performance
29
+ # characteristics, especially under high concurrency, than `Concurrent::Hash`.
30
+ # However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash`
31
+ # -- for instance, it does not necessarily retain ordering by insertion time as `Hash`
32
+ # does. For most uses it should do fine though, and we recommend you consider
33
+ # `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
34
+ class Map < Collection::MapImplementation
35
+
36
+ # @!macro map.atomic_method
37
+ # This method is atomic.
38
+
39
+ # @!macro map.atomic_method_with_block
40
+ # This method is atomic.
41
+ # @note Atomic methods taking a block do not allow the `self` instance
42
+ # to be used within the block. Doing so will cause a deadlock.
43
+
44
+ # @!method compute_if_absent(key)
45
+ # Compute and store new value for key if the key is absent.
46
+ # @param [Object] key
47
+ # @yield new value
48
+ # @yieldreturn [Object] new value
49
+ # @return [Object] new value or current value
50
+ # @!macro map.atomic_method_with_block
51
+
52
+ # @!method compute_if_present(key)
53
+ # Compute and store new value for key if the key is present.
54
+ # @param [Object] key
55
+ # @yield new value
56
+ # @yieldparam old_value [Object]
57
+ # @yieldreturn [Object, nil] new value, when nil the key is removed
58
+ # @return [Object, nil] new value or nil
59
+ # @!macro map.atomic_method_with_block
60
+
61
+ # @!method compute(key)
62
+ # Compute and store new value for key.
63
+ # @param [Object] key
64
+ # @yield compute new value from old one
65
+ # @yieldparam old_value [Object, nil] old_value, or nil when key is absent
66
+ # @yieldreturn [Object, nil] new value, when nil the key is removed
67
+ # @return [Object, nil] new value or nil
68
+ # @!macro map.atomic_method_with_block
69
+
70
+ # @!method merge_pair(key, value)
71
+ # If the key is absent, the value is stored, otherwise new value is
72
+ # computed with a block.
73
+ # @param [Object] key
74
+ # @param [Object] value
75
+ # @yield compute new value from old one
76
+ # @yieldparam old_value [Object] old value
77
+ # @yieldreturn [Object, nil] new value, when nil the key is removed
78
+ # @return [Object, nil] new value or nil
79
+ # @!macro map.atomic_method_with_block
80
+
81
+ # @!method replace_pair(key, old_value, new_value)
82
+ # Replaces old_value with new_value if key exists and current value
83
+ # matches old_value
84
+ # @param [Object] key
85
+ # @param [Object] old_value
86
+ # @param [Object] new_value
87
+ # @return [true, false] true if replaced
88
+ # @!macro map.atomic_method
89
+
90
+ # @!method replace_if_exists(key, new_value)
91
+ # Replaces current value with new_value if key exists
92
+ # @param [Object] key
93
+ # @param [Object] new_value
94
+ # @return [Object, nil] old value or nil
95
+ # @!macro map.atomic_method
96
+
97
+ # @!method get_and_set(key, value)
98
+ # Get the current value under key and set new value.
99
+ # @param [Object] key
100
+ # @param [Object] value
101
+ # @return [Object, nil] old value or nil when the key was absent
102
+ # @!macro map.atomic_method
103
+
104
+ # @!method delete(key)
105
+ # Delete key and its value.
106
+ # @param [Object] key
107
+ # @return [Object, nil] old value or nil when the key was absent
108
+ # @!macro map.atomic_method
109
+
110
+ # @!method delete_pair(key, value)
111
+ # Delete pair and its value if current value equals the provided value.
112
+ # @param [Object] key
113
+ # @param [Object] value
114
+ # @return [true, false] true if deleted
115
+ # @!macro map.atomic_method
116
+
117
+
118
+ def initialize(options = nil, &block)
119
+ if options.kind_of?(::Hash)
120
+ validate_options_hash!(options)
121
+ else
122
+ options = nil
123
+ end
124
+
125
+ super(options)
126
+ @default_proc = block
127
+ end
128
+
129
+ # Get a value with key
130
+ # @param [Object] key
131
+ # @return [Object] the value
132
+ def [](key)
133
+ if value = super # non-falsy value is an existing mapping, return it right away
134
+ value
135
+ # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
136
+ # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
137
+ # would be returned)
138
+ # note: nil == value check is not technically necessary
139
+ elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
140
+ @default_proc.call(self, key)
141
+ else
142
+ value
143
+ end
144
+ end
145
+
146
+ alias_method :get, :[]
147
+ # TODO (pitr-ch 30-Oct-2018): doc
148
+ alias_method :put, :[]=
149
+
150
+ # Get a value with key, or default_value when key is absent,
151
+ # or fail when no default_value is given.
152
+ # @param [Object] key
153
+ # @param [Object] default_value
154
+ # @yield default value for a key
155
+ # @yieldparam key [Object]
156
+ # @yieldreturn [Object] default value
157
+ # @return [Object] the value or default value
158
+ # @raise [KeyError] when key is missing and no default_value is provided
159
+ # @!macro map_method_not_atomic
160
+ # @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended
161
+ # to be use as a concurrency primitive with strong happens-before
162
+ # guarantees. It is not intended to be used as a high-level abstraction
163
+ # supporting complex operations. All read and write operations are
164
+ # thread safe, but no guarantees are made regarding race conditions
165
+ # between the fetch operation and yielding to the block. Additionally,
166
+ # this method does not support recursion. This is due to internal
167
+ # constraints that are very unlikely to change in the near future.
168
+ def fetch(key, default_value = NULL)
169
+ if NULL != (value = get_or_default(key, NULL))
170
+ value
171
+ elsif block_given?
172
+ yield key
173
+ elsif NULL != default_value
174
+ default_value
175
+ else
176
+ raise_fetch_no_key
177
+ end
178
+ end
179
+
180
+ # Fetch value with key, or store default value when key is absent,
181
+ # or fail when no default_value is given. This is a two step operation,
182
+ # therefore not atomic. The store can overwrite other concurrently
183
+ # stored value.
184
+ # @param [Object] key
185
+ # @param [Object] default_value
186
+ # @yield default value for a key
187
+ # @yieldparam key [Object]
188
+ # @yieldreturn [Object] default value
189
+ # @return [Object] the value or default value
190
+ # @!macro map.atomic_method_with_block
191
+ def fetch_or_store(key, default_value = NULL)
192
+ fetch(key) do
193
+ put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
194
+ end
195
+ end
196
+
197
+ # Insert value into map with key if key is absent in one atomic step.
198
+ # @param [Object] key
199
+ # @param [Object] value
200
+ # @return [Object, nil] the previous value when key was present or nil when there was no key
201
+ def put_if_absent(key, value)
202
+ computed = false
203
+ result = compute_if_absent(key) do
204
+ computed = true
205
+ value
206
+ end
207
+ computed ? nil : result
208
+ end unless method_defined?(:put_if_absent)
209
+
210
+ # Is the value stored in the map. Iterates over all values.
211
+ # @param [Object] value
212
+ # @return [true, false]
213
+ def value?(value)
214
+ each_value do |v|
215
+ return true if value.equal?(v)
216
+ end
217
+ false
218
+ end
219
+
220
+ # All keys
221
+ # @return [::Array<Object>] keys
222
+ def keys
223
+ arr = []
224
+ each_pair { |k, v| arr << k }
225
+ arr
226
+ end unless method_defined?(:keys)
227
+
228
+ # All values
229
+ # @return [::Array<Object>] values
230
+ def values
231
+ arr = []
232
+ each_pair { |k, v| arr << v }
233
+ arr
234
+ end unless method_defined?(:values)
235
+
236
+ # Iterates over each key.
237
+ # @yield for each key in the map
238
+ # @yieldparam key [Object]
239
+ # @return [self]
240
+ # @!macro map.atomic_method_with_block
241
+ def each_key
242
+ each_pair { |k, v| yield k }
243
+ end unless method_defined?(:each_key)
244
+
245
+ # Iterates over each value.
246
+ # @yield for each value in the map
247
+ # @yieldparam value [Object]
248
+ # @return [self]
249
+ # @!macro map.atomic_method_with_block
250
+ def each_value
251
+ each_pair { |k, v| yield v }
252
+ end unless method_defined?(:each_value)
253
+
254
+ # Iterates over each key value pair.
255
+ # @yield for each key value pair in the map
256
+ # @yieldparam key [Object]
257
+ # @yieldparam value [Object]
258
+ # @return [self]
259
+ # @!macro map.atomic_method_with_block
260
+ def each_pair
261
+ return enum_for :each_pair unless block_given?
262
+ super
263
+ end
264
+
265
+ alias_method :each, :each_pair unless method_defined?(:each)
266
+
267
+ # Find key of a value.
268
+ # @param [Object] value
269
+ # @return [Object, nil] key or nil when not found
270
+ def key(value)
271
+ each_pair { |k, v| return k if v == value }
272
+ nil
273
+ end unless method_defined?(:key)
274
+ alias_method :index, :key if RUBY_VERSION < '1.9'
275
+
276
+ # Is map empty?
277
+ # @return [true, false]
278
+ def empty?
279
+ each_pair { |k, v| return false }
280
+ true
281
+ end unless method_defined?(:empty?)
282
+
283
+ # The size of map.
284
+ # @return [Integer] size
285
+ def size
286
+ count = 0
287
+ each_pair { |k, v| count += 1 }
288
+ count
289
+ end unless method_defined?(:size)
290
+
291
+ # @!visibility private
292
+ def marshal_dump
293
+ raise TypeError, "can't dump hash with default proc" if @default_proc
294
+ h = {}
295
+ each_pair { |k, v| h[k] = v }
296
+ h
297
+ end
298
+
299
+ # @!visibility private
300
+ def marshal_load(hash)
301
+ initialize
302
+ populate_from(hash)
303
+ end
304
+
305
+ undef :freeze
306
+
307
+ # @!visibility private
308
+ def inspect
309
+ format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect
310
+ end
311
+
312
+ private
313
+
314
+ def raise_fetch_no_key
315
+ raise KeyError, 'key not found'
316
+ end
317
+
318
+ def initialize_copy(other)
319
+ super
320
+ populate_from(other)
321
+ end
322
+
323
+ def populate_from(hash)
324
+ hash.each_pair { |k, v| self[k] = v }
325
+ self
326
+ end
327
+
328
+ def validate_options_hash!(options)
329
+ if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0)
330
+ raise ArgumentError, ":initial_capacity must be a positive Integer"
331
+ end
332
+ if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
333
+ raise ArgumentError, ":load_factor must be a number between 0 and 1"
334
+ end
335
+ end
336
+ end
337
+ end