concurrent-ruby 1.0.5 → 1.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +203 -105
  6. data/Rakefile +278 -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 +304 -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.rb +1 -0
  23. data/lib/concurrent.rb +24 -20
  24. data/lib/concurrent/agent.rb +7 -7
  25. data/lib/concurrent/array.rb +59 -32
  26. data/lib/concurrent/async.rb +4 -4
  27. data/lib/concurrent/atom.rb +9 -9
  28. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  29. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  30. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  31. data/lib/concurrent/atomic/atomic_reference.rb +176 -33
  32. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  33. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  34. data/lib/concurrent/atomic/event.rb +1 -1
  35. data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
  36. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  37. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  38. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  39. data/lib/concurrent/atomic/semaphore.rb +8 -8
  40. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  41. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  42. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  43. data/lib/concurrent/atomics.rb +0 -43
  44. data/lib/concurrent/collection/lock_free_stack.rb +127 -0
  45. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  46. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  47. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  48. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  49. data/lib/concurrent/concern/logging.rb +6 -1
  50. data/lib/concurrent/concern/observable.rb +7 -7
  51. data/lib/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/concurrent/configuration.rb +1 -6
  53. data/lib/concurrent/constants.rb +1 -1
  54. data/lib/concurrent/dataflow.rb +2 -1
  55. data/lib/concurrent/delay.rb +9 -7
  56. data/lib/concurrent/exchanger.rb +13 -21
  57. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  58. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  59. data/lib/concurrent/executor/executor_service.rb +15 -15
  60. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  62. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  63. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  64. data/lib/concurrent/executor/timer_set.rb +1 -1
  65. data/lib/concurrent/future.rb +4 -1
  66. data/lib/concurrent/hash.rb +53 -30
  67. data/lib/concurrent/ivar.rb +5 -6
  68. data/lib/concurrent/map.rb +20 -25
  69. data/lib/concurrent/maybe.rb +1 -1
  70. data/lib/concurrent/mutable_struct.rb +15 -14
  71. data/lib/concurrent/mvar.rb +2 -2
  72. data/lib/concurrent/promise.rb +53 -21
  73. data/lib/concurrent/promises.rb +1938 -0
  74. data/lib/concurrent/re_include.rb +58 -0
  75. data/lib/concurrent/set.rb +66 -0
  76. data/lib/concurrent/settable_struct.rb +1 -0
  77. data/lib/concurrent/synchronization.rb +4 -5
  78. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  79. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  80. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  81. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  82. data/lib/concurrent/synchronization/object.rb +8 -4
  83. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  84. data/lib/concurrent/synchronization/volatile.rb +11 -9
  85. data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
  86. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  87. data/lib/concurrent/timer_task.rb +5 -2
  88. data/lib/concurrent/tuple.rb +1 -1
  89. data/lib/concurrent/tvar.rb +2 -2
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +2 -2
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. metadata +35 -21
  97. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  98. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  99. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  100. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  101. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  102. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  103. data/lib/concurrent/edge.rb +0 -26
  104. data/lib/concurrent/lazy_register.rb +0 -81
  105. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  106. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  107. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -1,16 +1,11 @@
1
- require 'concurrent/synchronization'
2
- require 'concurrent/atomic_reference/direct_update'
3
- require 'concurrent/atomic_reference/numeric_cas_wrapper'
4
-
5
1
  module Concurrent
6
2
 
7
- # @!macro atomic_reference
8
- #
9
3
  # @!visibility private
10
4
  # @!macro internal_implementation_note
11
5
  class MutexAtomicReference < Synchronization::LockableObject
12
- include Concurrent::AtomicDirectUpdate
13
- include Concurrent::AtomicNumericCompareAndSetWrapper
6
+ include AtomicDirectUpdate
7
+ include AtomicNumericCompareAndSetWrapper
8
+ alias_method :compare_and_swap, :compare_and_set
14
9
 
15
10
  # @!macro atomic_reference_method_initialize
16
11
  def initialize(value = nil)
@@ -23,6 +23,6 @@ module Concurrent
23
23
  _compare_and_set(old_value, new_value)
24
24
  end
25
25
  end
26
- alias_method :compare_and_swap, :compare_and_set
26
+
27
27
  end
28
28
  end
@@ -1,46 +1,3 @@
1
- # @!macro [new] atomic_reference
2
- #
3
- # An object reference that may be updated atomically. All read and write
4
- # operations have java volatile semantic.
5
- #
6
- # @!macro thread_safe_variable_comparison
7
- #
8
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
9
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
10
- #
11
- # @!method initialize
12
- # @!macro [new] atomic_reference_method_initialize
13
- # @param [Object] value The initial value.
14
- #
15
- # @!method get
16
- # @!macro [new] atomic_reference_method_get
17
- # Gets the current value.
18
- # @return [Object] the current value
19
- #
20
- # @!method set
21
- # @!macro [new] atomic_reference_method_set
22
- # Sets to the given value.
23
- # @param [Object] new_value the new value
24
- # @return [Object] the new value
25
- #
26
- # @!method get_and_set
27
- # @!macro [new] atomic_reference_method_get_and_set
28
- # Atomically sets to the given value and returns the old value.
29
- # @param [Object] new_value the new value
30
- # @return [Object] the old value
31
- #
32
- # @!method compare_and_set
33
- # @!macro [new] atomic_reference_method_compare_and_set
34
- #
35
- # Atomically sets the value to the given updated value if
36
- # the current value == the expected value.
37
- #
38
- # @param [Object] old_value the expected value
39
- # @param [Object] new_value the new value
40
- #
41
- # @return [Boolean] `true` if successful. A `false` return indicates
42
- # that the actual value was not equal to the expected value.
43
-
44
1
  require 'concurrent/atomic/atomic_reference'
45
2
  require 'concurrent/atomic/atomic_boolean'
46
3
  require 'concurrent/atomic/atomic_fixnum'
@@ -0,0 +1,127 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ class LockFreeStack < Synchronization::Object
5
+
6
+ safe_initialization!
7
+
8
+ class Node
9
+ # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
10
+
11
+ attr_reader :value, :next_node
12
+ # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
13
+ attr_writer :value
14
+
15
+ def initialize(value, next_node)
16
+ @value = value
17
+ @next_node = next_node
18
+ end
19
+
20
+ singleton_class.send :alias_method, :[], :new
21
+ end
22
+
23
+ class Empty < Node
24
+ def next_node
25
+ self
26
+ end
27
+ end
28
+
29
+ EMPTY = Empty[nil, nil]
30
+
31
+ attr_atomic(:head)
32
+ private :head, :head=, :swap_head, :compare_and_set_head, :update_head
33
+
34
+ def self.of1(value)
35
+ new Node[value, EMPTY]
36
+ end
37
+
38
+ def self.of2(value1, value2)
39
+ new Node[value1, Node[value2, EMPTY]]
40
+ end
41
+
42
+ def initialize(head = EMPTY)
43
+ super()
44
+ self.head = head
45
+ end
46
+
47
+ def empty?(head = self.head)
48
+ head.equal? EMPTY
49
+ end
50
+
51
+ def compare_and_push(head, value)
52
+ compare_and_set_head head, Node[value, head]
53
+ end
54
+
55
+ def push(value)
56
+ while true
57
+ current_head = head
58
+ return self if compare_and_set_head current_head, Node[value, current_head]
59
+ end
60
+ end
61
+
62
+ def peek
63
+ head
64
+ end
65
+
66
+ def compare_and_pop(head)
67
+ compare_and_set_head head, head.next_node
68
+ end
69
+
70
+ def pop
71
+ while true
72
+ current_head = head
73
+ return current_head.value if compare_and_set_head current_head, current_head.next_node
74
+ end
75
+ end
76
+
77
+ def compare_and_clear(head)
78
+ compare_and_set_head head, EMPTY
79
+ end
80
+
81
+ include Enumerable
82
+
83
+ def each(head = nil)
84
+ return to_enum(:each, head) unless block_given?
85
+ it = head || peek
86
+ until it.equal?(EMPTY)
87
+ yield it.value
88
+ it = it.next_node
89
+ end
90
+ self
91
+ end
92
+
93
+ def clear
94
+ while true
95
+ current_head = head
96
+ return false if current_head == EMPTY
97
+ return true if compare_and_set_head current_head, EMPTY
98
+ end
99
+ end
100
+
101
+ def clear_if(head)
102
+ compare_and_set_head head, EMPTY
103
+ end
104
+
105
+ def replace_if(head, new_head)
106
+ compare_and_set_head head, new_head
107
+ end
108
+
109
+ def clear_each(&block)
110
+ while true
111
+ current_head = head
112
+ return self if current_head == EMPTY
113
+ if compare_and_set_head current_head, EMPTY
114
+ each current_head, &block
115
+ return self
116
+ end
117
+ end
118
+ end
119
+
120
+ # @return [String] Short string representation.
121
+ def to_s
122
+ format '%s %s>', super[0..-2], to_a.to_s
123
+ end
124
+
125
+ alias_method :inspect, :to_s
126
+ end
127
+ end
@@ -289,7 +289,7 @@ module Concurrent
289
289
  end
290
290
  end
291
291
  elsif cas_hash(my_hash, my_hash | WAITING)
292
- force_aquire_lock(table, i)
292
+ force_acquire_lock(table, i)
293
293
  break
294
294
  end
295
295
  end
@@ -330,7 +330,7 @@ module Concurrent
330
330
  end
331
331
 
332
332
  private
333
- def force_aquire_lock(table, i)
333
+ def force_acquire_lock(table, i)
334
334
  cheap_synchronize do
335
335
  if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING
336
336
  cheap_wait
@@ -831,7 +831,7 @@ module Concurrent
831
831
  # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table
832
832
  redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder))
833
833
  elsif Node.locked_hash?(node_hash = node.hash)
834
- locked_indexes ||= Array.new
834
+ locked_indexes ||= ::Array.new
835
835
  if bin < 0 && locked_arr_idx > 0
836
836
  locked_arr_idx -= 1
837
837
  i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin
@@ -10,7 +10,7 @@ module Concurrent
10
10
 
11
11
  # WARNING: all public methods of the class must operate on the @backend
12
12
  # directly without calling each other. This is important because of the
13
- # SynchronizedMapBackend which uses a non-reentrant mutex for perfomance
13
+ # SynchronizedMapBackend which uses a non-reentrant mutex for performance
14
14
  # reasons.
15
15
  def initialize(options = nil)
16
16
  @backend = {}
@@ -95,7 +95,6 @@ module Concurrent
95
95
  end
96
96
 
97
97
  def each_pair
98
- return enum_for :each_pair unless block_given?
99
98
  dupped_backend.each_pair do |k, v|
100
99
  yield k, v
101
100
  end
@@ -15,7 +15,7 @@ module Concurrent
15
15
  end
16
16
  private_constant :NonConcurrentPriorityQueueImplementation
17
17
 
18
- # @!macro [attach] priority_queue
18
+ # @!macro priority_queue
19
19
  #
20
20
  # A queue collection in which the elements are sorted based on their
21
21
  # comparison (spaceship) operator `<=>`. Items are added to the queue
@@ -45,7 +45,7 @@ module Concurrent
45
45
  # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
46
46
  #
47
47
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
48
- #
48
+ #
49
49
  # @!visibility private
50
50
  class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation
51
51
 
@@ -60,83 +60,83 @@ module Concurrent
60
60
  alias_method :enq, :push
61
61
 
62
62
  # @!method initialize(opts = {})
63
- # @!macro [new] priority_queue_method_initialize
63
+ # @!macro priority_queue_method_initialize
64
64
  #
65
65
  # Create a new priority queue with no items.
66
- #
66
+ #
67
67
  # @param [Hash] opts the options for creating the queue
68
68
  # @option opts [Symbol] :order (:max) dictates the order in which items are
69
69
  # stored: from highest to lowest when `:max` or `:high`; from lowest to
70
70
  # highest when `:min` or `:low`
71
71
 
72
72
  # @!method clear
73
- # @!macro [new] priority_queue_method_clear
73
+ # @!macro priority_queue_method_clear
74
74
  #
75
75
  # Removes all of the elements from this priority queue.
76
76
 
77
77
  # @!method delete(item)
78
- # @!macro [new] priority_queue_method_delete
78
+ # @!macro priority_queue_method_delete
79
79
  #
80
80
  # Deletes all items from `self` that are equal to `item`.
81
- #
81
+ #
82
82
  # @param [Object] item the item to be removed from the queue
83
83
  # @return [Object] true if the item is found else false
84
84
 
85
85
  # @!method empty?
86
- # @!macro [new] priority_queue_method_empty
87
- #
86
+ # @!macro priority_queue_method_empty
87
+ #
88
88
  # Returns `true` if `self` contains no elements.
89
- #
89
+ #
90
90
  # @return [Boolean] true if there are no items in the queue else false
91
91
 
92
92
  # @!method include?(item)
93
- # @!macro [new] priority_queue_method_include
93
+ # @!macro priority_queue_method_include
94
94
  #
95
95
  # Returns `true` if the given item is present in `self` (that is, if any
96
96
  # element == `item`), otherwise returns false.
97
- #
97
+ #
98
98
  # @param [Object] item the item to search for
99
- #
99
+ #
100
100
  # @return [Boolean] true if the item is found else false
101
101
 
102
102
  # @!method length
103
- # @!macro [new] priority_queue_method_length
104
- #
103
+ # @!macro priority_queue_method_length
104
+ #
105
105
  # The current length of the queue.
106
- #
106
+ #
107
107
  # @return [Fixnum] the number of items in the queue
108
108
 
109
109
  # @!method peek
110
- # @!macro [new] priority_queue_method_peek
111
- #
110
+ # @!macro priority_queue_method_peek
111
+ #
112
112
  # Retrieves, but does not remove, the head of this queue, or returns `nil`
113
113
  # if this queue is empty.
114
- #
114
+ #
115
115
  # @return [Object] the head of the queue or `nil` when empty
116
116
 
117
117
  # @!method pop
118
- # @!macro [new] priority_queue_method_pop
119
- #
118
+ # @!macro priority_queue_method_pop
119
+ #
120
120
  # Retrieves and removes the head of this queue, or returns `nil` if this
121
121
  # queue is empty.
122
- #
122
+ #
123
123
  # @return [Object] the head of the queue or `nil` when empty
124
124
 
125
125
  # @!method push(item)
126
- # @!macro [new] priority_queue_method_push
127
- #
126
+ # @!macro priority_queue_method_push
127
+ #
128
128
  # Inserts the specified element into this priority queue.
129
- #
129
+ #
130
130
  # @param [Object] item the item to insert onto the queue
131
131
 
132
132
  # @!method self.from_list(list, opts = {})
133
- # @!macro [new] priority_queue_method_from_list
134
- #
133
+ # @!macro priority_queue_method_from_list
134
+ #
135
135
  # Create a new priority queue from the given list.
136
- #
136
+ #
137
137
  # @param [Enumerable] list the list to build the queue from
138
138
  # @param [Hash] opts the options for creating the queue
139
- #
139
+ #
140
140
  # @return [NonConcurrentPriorityQueue] the newly created and populated queue
141
141
  end
142
142
  end
@@ -32,7 +32,7 @@ module Concurrent
32
32
  synchronize{ @value = value }
33
33
  end
34
34
 
35
- # @!macro [attach] dereferenceable_set_deref_options
35
+ # @!macro dereferenceable_set_deref_options
36
36
  # Set the options which define the operations #value performs before
37
37
  # returning data to the caller (dereferencing).
38
38
  #
@@ -17,7 +17,12 @@ module Concurrent
17
17
  def log(level, progname, message = nil, &block)
18
18
  #NOTE: Cannot require 'concurrent/configuration' above due to circular references.
19
19
  # Assume that the gem has been initialized if we've gotten this far.
20
- (@logger || Concurrent.global_logger).call level, progname, message, &block
20
+ logger = if defined?(@logger) && @logger
21
+ @logger
22
+ else
23
+ Concurrent.global_logger
24
+ end
25
+ logger.call level, progname, message, &block
21
26
  rescue => error
22
27
  $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
23
28
  "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
@@ -49,7 +49,7 @@ module Concurrent
49
49
  # or an AtomicFixum)
50
50
  module Observable
51
51
 
52
- # @!macro [attach] observable_add_observer
52
+ # @!macro observable_add_observer
53
53
  #
54
54
  # Adds an observer to this set. If a block is passed, the observer will be
55
55
  # created by this method and no other params should be passed.
@@ -72,8 +72,8 @@ module Concurrent
72
72
  self
73
73
  end
74
74
 
75
- # @!macro [attach] observable_delete_observer
76
- #
75
+ # @!macro observable_delete_observer
76
+ #
77
77
  # Remove `observer` as an observer on this object so that it will no
78
78
  # longer receive notifications.
79
79
  #
@@ -83,17 +83,17 @@ module Concurrent
83
83
  observers.delete_observer(observer)
84
84
  end
85
85
 
86
- # @!macro [attach] observable_delete_observers
87
- #
86
+ # @!macro observable_delete_observers
87
+ #
88
88
  # Remove all observers associated with this object.
89
- #
89
+ #
90
90
  # @return [Observable] self
91
91
  def delete_observers
92
92
  observers.delete_observers
93
93
  self
94
94
  end
95
95
 
96
- # @!macro [attach] observable_count_observers
96
+ # @!macro observable_count_observers
97
97
  #
98
98
  # Return the number of observers associated with this object.
99
99
  #
@@ -175,13 +175,8 @@ module Concurrent
175
175
  end
176
176
 
177
177
  def self.new_io_executor(opts = {})
178
- ThreadPoolExecutor.new(
179
- min_threads: [2, Concurrent.processor_count].max,
180
- max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
181
- # max_threads: 1000,
178
+ CachedThreadPool.new(
182
179
  auto_terminate: opts.fetch(:auto_terminate, true),
183
- idletime: 60, # 1 minute
184
- max_queue: 0, # unlimited
185
180
  fallback_policy: :abort # shouldn't matter -- 0 max queue
186
181
  )
187
182
  end