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,88 @@
1
+ require 'concurrent/thread_safe/util'
2
+
3
+ # Shim for TruffleRuby.synchronized
4
+ if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
5
+ module TruffleRuby
6
+ def self.synchronized(object, &block)
7
+ Truffle::System.synchronized(object, &block)
8
+ end
9
+ end
10
+ end
11
+
12
+ module Concurrent
13
+ module ThreadSafe
14
+ module Util
15
+ def self.make_synchronized_on_cruby(klass)
16
+ klass.class_eval do
17
+ def initialize(*args, &block)
18
+ @_monitor = Monitor.new
19
+ super
20
+ end
21
+
22
+ def initialize_copy(other)
23
+ # make sure a copy is not sharing a monitor with the original object!
24
+ @_monitor = Monitor.new
25
+ super
26
+ end
27
+ end
28
+
29
+ klass.superclass.instance_methods(false).each do |method|
30
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
31
+ def #{method}(*args)
32
+ monitor = @_monitor
33
+ monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
34
+ monitor.synchronize { super }
35
+ end
36
+ RUBY
37
+ end
38
+ end
39
+
40
+ def self.make_synchronized_on_rbx(klass)
41
+ klass.class_eval do
42
+ private
43
+
44
+ def _mon_initialize
45
+ @_monitor ||= Monitor.new # avoid double initialisation
46
+ end
47
+
48
+ def self.new(*args)
49
+ obj = super(*args)
50
+ obj.send(:_mon_initialize)
51
+ obj
52
+ end
53
+ end
54
+
55
+ klass.superclass.instance_methods(false).each do |method|
56
+ case method
57
+ when :new_range, :new_reserved
58
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
59
+ def #{method}(*args)
60
+ obj = super
61
+ obj.send(:_mon_initialize)
62
+ obj
63
+ end
64
+ RUBY
65
+ else
66
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
67
+ def #{method}(*args)
68
+ monitor = @_monitor
69
+ monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
70
+ monitor.synchronize { super }
71
+ end
72
+ RUBY
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.make_synchronized_on_truffleruby(klass)
78
+ klass.superclass.instance_methods(false).each do |method|
79
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
80
+ def #{method}(*args, &block)
81
+ TruffleRuby.synchronized(self) { super(*args, &block) }
82
+ end
83
+ RUBY
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,38 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/tuple'
3
+
4
+ module Concurrent
5
+
6
+ # @!visibility private
7
+ module ThreadSafe
8
+
9
+ # @!visibility private
10
+ module Util
11
+
12
+ # @!visibility private
13
+ class PowerOfTwoTuple < Concurrent::Tuple
14
+
15
+ def initialize(size)
16
+ raise ArgumentError, "size must be a power of 2 (#{size.inspect} provided)" unless size > 0 && size & (size - 1) == 0
17
+ super(size)
18
+ end
19
+
20
+ def hash_to_index(hash)
21
+ (size - 1) & hash
22
+ end
23
+
24
+ def volatile_get_by_hash(hash)
25
+ volatile_get(hash_to_index(hash))
26
+ end
27
+
28
+ def volatile_set_by_hash(hash, value)
29
+ volatile_set(hash_to_index(hash), value)
30
+ end
31
+
32
+ def next_in_size_table
33
+ self.class.new(size << 1)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,246 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/power_of_two_tuple'
3
+ require 'concurrent/thread_safe/util/volatile'
4
+ require 'concurrent/thread_safe/util/xor_shift_random'
5
+
6
+ module Concurrent
7
+
8
+ # @!visibility private
9
+ module ThreadSafe
10
+
11
+ # @!visibility private
12
+ module Util
13
+
14
+ # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6
15
+ # available in public domain.
16
+ #
17
+ # Original source code available here:
18
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6
19
+ #
20
+ # Class holding common representation and mechanics for classes supporting
21
+ # dynamic striping on 64bit values.
22
+ #
23
+ # This class maintains a lazily-initialized table of atomically updated
24
+ # variables, plus an extra +base+ field. The table size is a power of two.
25
+ # Indexing uses masked per-thread hash codes. Nearly all methods on this
26
+ # class are private, accessed directly by subclasses.
27
+ #
28
+ # Table entries are of class +Cell+; a variant of AtomicLong padded to
29
+ # reduce cache contention on most processors. Padding is overkill for most
30
+ # Atomics because they are usually irregularly scattered in memory and thus
31
+ # don't interfere much with each other. But Atomic objects residing in
32
+ # arrays will tend to be placed adjacent to each other, and so will most
33
+ # often share cache lines (with a huge negative performance impact) without
34
+ # this precaution.
35
+ #
36
+ # In part because +Cell+s are relatively large, we avoid creating them until
37
+ # they are needed. When there is no contention, all updates are made to the
38
+ # +base+ field. Upon first contention (a failed CAS on +base+ update), the
39
+ # table is initialized to size 2. The table size is doubled upon further
40
+ # contention until reaching the nearest power of two greater than or equal
41
+ # to the number of CPUS. Table slots remain empty (+nil+) until they are
42
+ # needed.
43
+ #
44
+ # A single spinlock (+busy+) is used for initializing and resizing the
45
+ # table, as well as populating slots with new +Cell+s. There is no need for
46
+ # a blocking lock: When the lock is not available, threads try other slots
47
+ # (or the base). During these retries, there is increased contention and
48
+ # reduced locality, which is still better than alternatives.
49
+ #
50
+ # Per-thread hash codes are initialized to random values. Contention and/or
51
+ # table collisions are indicated by failed CASes when performing an update
52
+ # operation (see method +retry_update+). Upon a collision, if the table size
53
+ # is less than the capacity, it is doubled in size unless some other thread
54
+ # holds the lock. If a hashed slot is empty, and lock is available, a new
55
+ # +Cell+ is created. Otherwise, if the slot exists, a CAS is tried. Retries
56
+ # proceed by "double hashing", using a secondary hash (XorShift) to try to
57
+ # find a free slot.
58
+ #
59
+ # The table size is capped because, when there are more threads than CPUs,
60
+ # supposing that each thread were bound to a CPU, there would exist a
61
+ # perfect hash function mapping threads to slots that eliminates collisions.
62
+ # When we reach capacity, we search for this mapping by randomly varying the
63
+ # hash codes of colliding threads. Because search is random, and collisions
64
+ # only become known via CAS failures, convergence can be slow, and because
65
+ # threads are typically not bound to CPUS forever, may not occur at all.
66
+ # However, despite these limitations, observed contention rates are
67
+ # typically low in these cases.
68
+ #
69
+ # It is possible for a +Cell+ to become unused when threads that once hashed
70
+ # to it terminate, as well as in the case where doubling the table causes no
71
+ # thread to hash to it under expanded mask. We do not try to detect or
72
+ # remove such cells, under the assumption that for long-running instances,
73
+ # observed contention levels will recur, so the cells will eventually be
74
+ # needed again; and for short-lived ones, it does not matter.
75
+ #
76
+ # @!visibility private
77
+ class Striped64
78
+
79
+ # Padded variant of AtomicLong supporting only raw accesses plus CAS.
80
+ # The +value+ field is placed between pads, hoping that the JVM doesn't
81
+ # reorder them.
82
+ #
83
+ # Optimisation note: It would be possible to use a release-only
84
+ # form of CAS here, if it were provided.
85
+ #
86
+ # @!visibility private
87
+ class Cell < Concurrent::AtomicReference
88
+
89
+ alias_method :cas, :compare_and_set
90
+
91
+ def cas_computed
92
+ cas(current_value = value, yield(current_value))
93
+ end
94
+
95
+ # @!visibility private
96
+ def self.padding
97
+ # TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot
98
+ # TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created
99
+ # hide from yardoc in a method
100
+ attr_reader :padding_0, :padding_1, :padding_2, :padding_3, :padding_4, :padding_5, :padding_6, :padding_7, :padding_8, :padding_9, :padding_10, :padding_11
101
+ end
102
+ padding
103
+ end
104
+
105
+ extend Volatile
106
+ attr_volatile :cells, # Table of cells. When non-null, size is a power of 2.
107
+ :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS.
108
+ :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells.
109
+
110
+ alias_method :busy?, :busy
111
+
112
+ def initialize
113
+ super()
114
+ self.busy = false
115
+ self.base = 0
116
+ end
117
+
118
+ # Handles cases of updates involving initialization, resizing,
119
+ # creating new Cells, and/or contention. See above for
120
+ # explanation. This method suffers the usual non-modularity
121
+ # problems of optimistic retry code, relying on rechecked sets of
122
+ # reads.
123
+ #
124
+ # Arguments:
125
+ # [+x+]
126
+ # the value
127
+ # [+hash_code+]
128
+ # hash code used
129
+ # [+x+]
130
+ # false if CAS failed before call
131
+ def retry_update(x, hash_code, was_uncontended) # :yields: current_value
132
+ hash = hash_code
133
+ collided = false # True if last slot nonempty
134
+ while true
135
+ if current_cells = cells
136
+ if !(cell = current_cells.volatile_get_by_hash(hash))
137
+ if busy?
138
+ collided = false
139
+ else # Try to attach new Cell
140
+ if try_to_install_new_cell(Cell.new(x), hash) # Optimistically create and try to insert new cell
141
+ break
142
+ else
143
+ redo # Slot is now non-empty
144
+ end
145
+ end
146
+ elsif !was_uncontended # CAS already known to fail
147
+ was_uncontended = true # Continue after rehash
148
+ elsif cell.cas_computed {|current_value| yield current_value}
149
+ break
150
+ elsif current_cells.size >= CPU_COUNT || cells != current_cells # At max size or stale
151
+ collided = false
152
+ elsif collided && expand_table_unless_stale(current_cells)
153
+ collided = false
154
+ redo # Retry with expanded table
155
+ else
156
+ collided = true
157
+ end
158
+ hash = XorShiftRandom.xorshift(hash)
159
+
160
+ elsif try_initialize_cells(x, hash) || cas_base_computed {|current_base| yield current_base}
161
+ break
162
+ end
163
+ end
164
+ self.hash_code = hash
165
+ end
166
+
167
+ private
168
+ # Static per-thread hash code key. Shared across all instances to
169
+ # reduce Thread locals pollution and because adjustments due to
170
+ # collisions in one table are likely to be appropriate for
171
+ # others.
172
+ THREAD_LOCAL_KEY = "#{name}.hash_code".to_sym
173
+
174
+ # A thread-local hash code accessor. The code is initially
175
+ # random, but may be set to a different value upon collisions.
176
+ def hash_code
177
+ Thread.current[THREAD_LOCAL_KEY] ||= XorShiftRandom.get
178
+ end
179
+
180
+ def hash_code=(hash)
181
+ Thread.current[THREAD_LOCAL_KEY] = hash
182
+ end
183
+
184
+ # Sets base and all +cells+ to the given value.
185
+ def internal_reset(initial_value)
186
+ current_cells = cells
187
+ self.base = initial_value
188
+ if current_cells
189
+ current_cells.each do |cell|
190
+ cell.value = initial_value if cell
191
+ end
192
+ end
193
+ end
194
+
195
+ def cas_base_computed
196
+ cas_base(current_base = base, yield(current_base))
197
+ end
198
+
199
+ def free?
200
+ !busy?
201
+ end
202
+
203
+ def try_initialize_cells(x, hash)
204
+ if free? && !cells
205
+ try_in_busy do
206
+ unless cells # Recheck under lock
207
+ new_cells = PowerOfTwoTuple.new(2)
208
+ new_cells.volatile_set_by_hash(hash, Cell.new(x))
209
+ self.cells = new_cells
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ def expand_table_unless_stale(current_cells)
216
+ try_in_busy do
217
+ if current_cells == cells # Recheck under lock
218
+ new_cells = current_cells.next_in_size_table
219
+ current_cells.each_with_index {|x, i| new_cells.volatile_set(i, x)}
220
+ self.cells = new_cells
221
+ end
222
+ end
223
+ end
224
+
225
+ def try_to_install_new_cell(new_cell, hash)
226
+ try_in_busy do
227
+ # Recheck under lock
228
+ if (current_cells = cells) && !current_cells.volatile_get(i = current_cells.hash_to_index(hash))
229
+ current_cells.volatile_set(i, new_cell)
230
+ end
231
+ end
232
+ end
233
+
234
+ def try_in_busy
235
+ if cas_busy(false, true)
236
+ begin
237
+ yield
238
+ ensure
239
+ self.busy = false
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,75 @@
1
+ require 'concurrent/thread_safe/util'
2
+
3
+ module Concurrent
4
+
5
+ # @!visibility private
6
+ module ThreadSafe
7
+
8
+ # @!visibility private
9
+ module Util
10
+
11
+ # @!visibility private
12
+ module Volatile
13
+
14
+ # Provides +volatile+ (in the JVM's sense) attribute accessors implemented
15
+ # atop of +Concurrent::AtomicReference+.
16
+ #
17
+ # Usage:
18
+ # class Foo
19
+ # extend Concurrent::ThreadSafe::Util::Volatile
20
+ # attr_volatile :foo, :bar
21
+ #
22
+ # def initialize(bar)
23
+ # super() # must super() into parent initializers before using the volatile attribute accessors
24
+ # self.bar = bar
25
+ # end
26
+ #
27
+ # def hello
28
+ # my_foo = foo # volatile read
29
+ # self.foo = 1 # volatile write
30
+ # cas_foo(1, 2) # => true | a strong CAS
31
+ # end
32
+ # end
33
+ def attr_volatile(*attr_names)
34
+ return if attr_names.empty?
35
+ include(Module.new do
36
+ atomic_ref_setup = attr_names.map {|attr_name| "@__#{attr_name} = Concurrent::AtomicReference.new"}
37
+ initialize_copy_setup = attr_names.zip(atomic_ref_setup).map do |attr_name, ref_setup|
38
+ "#{ref_setup}(other.instance_variable_get(:@__#{attr_name}).get)"
39
+ end
40
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
41
+ def initialize(*)
42
+ super
43
+ #{atomic_ref_setup.join('; ')}
44
+ end
45
+
46
+ def initialize_copy(other)
47
+ super
48
+ #{initialize_copy_setup.join('; ')}
49
+ end
50
+ RUBY_EVAL
51
+
52
+ attr_names.each do |attr_name|
53
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
54
+ def #{attr_name}
55
+ @__#{attr_name}.get
56
+ end
57
+
58
+ def #{attr_name}=(value)
59
+ @__#{attr_name}.set(value)
60
+ end
61
+
62
+ def compare_and_set_#{attr_name}(old_value, new_value)
63
+ @__#{attr_name}.compare_and_set(old_value, new_value)
64
+ end
65
+ RUBY_EVAL
66
+
67
+ alias_method :"cas_#{attr_name}", :"compare_and_set_#{attr_name}"
68
+ alias_method :"lazy_set_#{attr_name}", :"#{attr_name}="
69
+ end
70
+ end)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,50 @@
1
+ require 'concurrent/thread_safe/util'
2
+
3
+ module Concurrent
4
+
5
+ # @!visibility private
6
+ module ThreadSafe
7
+
8
+ # @!visibility private
9
+ module Util
10
+
11
+ # A xorshift random number (positive +Fixnum+s) generator, provides
12
+ # reasonably cheap way to generate thread local random numbers without
13
+ # contending for the global +Kernel.rand+.
14
+ #
15
+ # Usage:
16
+ # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed
17
+ # while true
18
+ # if (x = XorShiftRandom.xorshift).odd? # thread-localy generate a next random number
19
+ # do_something_at_random
20
+ # end
21
+ # end
22
+ module XorShiftRandom
23
+ extend self
24
+ MAX_XOR_SHIFTABLE_INT = MAX_INT - 1
25
+
26
+ # Generates an initial non-zero positive +Fixnum+ via +Kernel.rand+.
27
+ def get
28
+ Kernel.rand(MAX_XOR_SHIFTABLE_INT) + 1 # 0 can't be xorshifted
29
+ end
30
+
31
+ # xorshift based on: http://www.jstatsoft.org/v08/i14/paper
32
+ if 0.size == 4
33
+ # using the "yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows
34
+ def xorshift(x)
35
+ x ^= x >> 3
36
+ x ^= (x << 1) & MAX_INT # cut-off Bignum overflow
37
+ x ^= x >> 14
38
+ end
39
+ else
40
+ # using the "yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;" transform with the (a,b,c) tuple with values (1,1,54) to minimise Bignum overflows
41
+ def xorshift(x)
42
+ x ^= x >> 1
43
+ x ^= (x << 1) & MAX_INT # cut-off Bignum overflow
44
+ x ^= x >> 54
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,16 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ module ThreadSafe
5
+
6
+ # @!visibility private
7
+ module Util
8
+
9
+ # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger
10
+ FIXNUM_BIT_SIZE = (0.size * 8) - 2
11
+ MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1
12
+ # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter
13
+ CPU_COUNT = 16 # is there a way to determine this?
14
+ end
15
+ end
16
+ end