concurrent-ruby 1.1.5 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/Gemfile +5 -10
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +55 -31
  6. data/Rakefile +84 -92
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  10. data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +2 -1
  11. data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +6 -16
  12. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +10 -20
  13. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +2 -2
  14. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +7 -6
  15. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +5 -4
  16. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +3 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +135 -0
  18. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
  19. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
  20. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  21. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +1 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +11 -5
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +11 -5
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -1
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +19 -3
  28. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
  29. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +9 -9
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +32 -14
  31. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +111 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +15 -4
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +1 -1
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +1 -1
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +2 -0
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +3 -3
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +16 -8
  39. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
  42. data/lib/concurrent-ruby/concurrent/concern/logging.rb +116 -0
  43. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  44. data/lib/concurrent-ruby/concurrent/configuration.rb +105 -0
  45. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +2 -2
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +5 -0
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +1 -0
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +34 -37
  49. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +4 -4
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +2 -2
  51. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +29 -15
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +21 -9
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +19 -2
  55. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +6 -6
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +1 -1
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +5 -2
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +1 -0
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +2 -1
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +0 -1
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +1 -10
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +10 -2
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +2 -1
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +44 -31
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +13 -3
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +1 -1
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +2 -1
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +7 -6
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +2 -0
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +30 -17
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +17 -19
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +13 -3
  77. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -1
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +1 -3
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +11 -0
  80. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  81. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +3 -1
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -7
  85. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +18 -5
  86. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +12 -44
  87. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  88. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +101 -0
  89. data/lib/concurrent-ruby/concurrent/synchronization.rb +13 -0
  90. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +47 -0
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +2 -39
  92. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +52 -0
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +11 -34
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -5
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -57
  97. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +5 -16
  98. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +19 -0
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +8 -10
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +1 -0
  101. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +110 -0
  102. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  103. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  104. metadata +127 -129
  105. data/lib/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  106. data/lib/concurrent/atomic/atomic_reference.rb +0 -204
  107. data/lib/concurrent/atomic/java_thread_local_var.rb +0 -37
  108. data/lib/concurrent/atomic/ruby_thread_local_var.rb +0 -161
  109. data/lib/concurrent/atomic/thread_local_var.rb +0 -104
  110. data/lib/concurrent/concern/logging.rb +0 -32
  111. data/lib/concurrent/concurrent_ruby.jar +0 -0
  112. data/lib/concurrent/configuration.rb +0 -184
  113. data/lib/concurrent/synchronization/jruby_object.rb +0 -45
  114. data/lib/concurrent/synchronization/mri_object.rb +0 -44
  115. data/lib/concurrent/synchronization/rbx_lockable_object.rb +0 -65
  116. data/lib/concurrent/synchronization/rbx_object.rb +0 -49
  117. data/lib/concurrent/synchronization/truffleruby_object.rb +0 -47
  118. data/lib/concurrent/synchronization/volatile.rb +0 -36
  119. data/lib/concurrent/synchronization.rb +0 -30
  120. data/lib/concurrent/thread_safe/synchronized_delegator.rb +0 -50
  121. data/lib/concurrent/thread_safe/util/data_structures.rb +0 -63
  122. data/lib/concurrent/utility/at_exit.rb +0 -97
  123. data/lib/concurrent/utility/monotonic_time.rb +0 -58
  124. data/lib/concurrent/utility/processor_counter.rb +0 -158
  125. data/lib/concurrent/version.rb +0 -3
  126. data/lib/concurrent-ruby.rb +0 -1
  127. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
  128. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +0 -0
  129. data/lib/{concurrent → concurrent-ruby/concurrent}/atomics.rb +0 -0
  130. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  131. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +0 -0
  132. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  133. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +1 -1
  134. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  135. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  136. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +0 -0
  137. /data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +0 -0
  138. /data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
  139. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  140. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  141. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  142. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  143. /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  144. /data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +0 -0
  145. /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  146. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  147. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  148. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  149. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  150. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  151. /data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
@@ -1,5 +1,6 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
1
3
  require 'concurrent/atomic/mutex_atomic_boolean'
2
- require 'concurrent/synchronization'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -41,13 +42,13 @@ module Concurrent
41
42
  #
42
43
  # Explicitly sets the value to true.
43
44
  #
44
- # @return [Boolean] true is value has changed, otherwise false
45
+ # @return [Boolean] true if value has changed, otherwise false
45
46
 
46
47
  # @!macro atomic_boolean_method_make_false
47
48
  #
48
49
  # Explicitly sets the value to false.
49
50
  #
50
- # @return [Boolean] true is value has changed, otherwise false
51
+ # @return [Boolean] true if value has changed, otherwise false
51
52
 
52
53
  ###################################################################
53
54
 
@@ -79,10 +80,10 @@ module Concurrent
79
80
  # @!visibility private
80
81
  # @!macro internal_implementation_note
81
82
  AtomicBooleanImplementation = case
82
- when defined?(JavaAtomicBoolean)
83
- JavaAtomicBoolean
84
- when defined?(CAtomicBoolean)
83
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
85
84
  CAtomicBoolean
85
+ when Concurrent.on_jruby?
86
+ JavaAtomicBoolean
86
87
  else
87
88
  MutexAtomicBoolean
88
89
  end
@@ -1,5 +1,6 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
1
3
  require 'concurrent/atomic/mutex_atomic_fixnum'
2
- require 'concurrent/synchronization'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -96,10 +97,10 @@ module Concurrent
96
97
  # @!visibility private
97
98
  # @!macro internal_implementation_note
98
99
  AtomicFixnumImplementation = case
99
- when defined?(JavaAtomicFixnum)
100
- JavaAtomicFixnum
101
- when defined?(CAtomicFixnum)
100
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
102
101
  CAtomicFixnum
102
+ when Concurrent.on_jruby?
103
+ JavaAtomicFixnum
103
104
  else
104
105
  MutexAtomicFixnum
105
106
  end
@@ -1,3 +1,6 @@
1
+ require 'concurrent/errors'
2
+ require 'concurrent/synchronization/object'
3
+
1
4
  module Concurrent
2
5
  # An atomic reference which maintains an object reference along with a mark bit
3
6
  # that can be updated atomically.
@@ -0,0 +1,135 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
3
+ require 'concurrent/atomic_reference/atomic_direct_update'
4
+ require 'concurrent/atomic_reference/numeric_cas_wrapper'
5
+ require 'concurrent/atomic_reference/mutex_atomic'
6
+
7
+ # Shim for TruffleRuby::AtomicReference
8
+ if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
9
+ # @!visibility private
10
+ module TruffleRuby
11
+ AtomicReference = Truffle::AtomicReference
12
+ end
13
+ end
14
+
15
+ module Concurrent
16
+
17
+ # @!macro internal_implementation_note
18
+ AtomicReferenceImplementation = case
19
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
20
+ # @!visibility private
21
+ # @!macro internal_implementation_note
22
+ class CAtomicReference
23
+ include AtomicDirectUpdate
24
+ include AtomicNumericCompareAndSetWrapper
25
+ alias_method :compare_and_swap, :compare_and_set
26
+ end
27
+ CAtomicReference
28
+ when Concurrent.on_jruby?
29
+ # @!visibility private
30
+ # @!macro internal_implementation_note
31
+ class JavaAtomicReference
32
+ include AtomicDirectUpdate
33
+ end
34
+ JavaAtomicReference
35
+ when Concurrent.on_truffleruby?
36
+ class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
37
+ include AtomicDirectUpdate
38
+ alias_method :value, :get
39
+ alias_method :value=, :set
40
+ alias_method :compare_and_swap, :compare_and_set
41
+ alias_method :swap, :get_and_set
42
+ end
43
+ TruffleRubyAtomicReference
44
+ else
45
+ MutexAtomicReference
46
+ end
47
+ private_constant :AtomicReferenceImplementation
48
+
49
+ # An object reference that may be updated atomically. All read and write
50
+ # operations have java volatile semantic.
51
+ #
52
+ # @!macro thread_safe_variable_comparison
53
+ #
54
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
55
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
56
+ #
57
+ # @!method initialize(value = nil)
58
+ # @!macro atomic_reference_method_initialize
59
+ # @param [Object] value The initial value.
60
+ #
61
+ # @!method get
62
+ # @!macro atomic_reference_method_get
63
+ # Gets the current value.
64
+ # @return [Object] the current value
65
+ #
66
+ # @!method set(new_value)
67
+ # @!macro atomic_reference_method_set
68
+ # Sets to the given value.
69
+ # @param [Object] new_value the new value
70
+ # @return [Object] the new value
71
+ #
72
+ # @!method get_and_set(new_value)
73
+ # @!macro atomic_reference_method_get_and_set
74
+ # Atomically sets to the given value and returns the old value.
75
+ # @param [Object] new_value the new value
76
+ # @return [Object] the old value
77
+ #
78
+ # @!method compare_and_set(old_value, new_value)
79
+ # @!macro atomic_reference_method_compare_and_set
80
+ #
81
+ # Atomically sets the value to the given updated value if
82
+ # the current value == the expected value.
83
+ #
84
+ # @param [Object] old_value the expected value
85
+ # @param [Object] new_value the new value
86
+ #
87
+ # @return [Boolean] `true` if successful. A `false` return indicates
88
+ # that the actual value was not equal to the expected value.
89
+ #
90
+ # @!method update
91
+ # Pass the current value to the given block, replacing it
92
+ # with the block's result. May retry if the value changes
93
+ # during the block's execution.
94
+ #
95
+ # @yield [Object] Calculate a new value for the atomic reference using
96
+ # given (old) value
97
+ # @yieldparam [Object] old_value the starting value of the atomic reference
98
+ # @return [Object] the new value
99
+ #
100
+ # @!method try_update
101
+ # Pass the current value to the given block, replacing it
102
+ # with the block's result. Return nil if the update fails.
103
+ #
104
+ # @yield [Object] Calculate a new value for the atomic reference using
105
+ # given (old) value
106
+ # @yieldparam [Object] old_value the starting value of the atomic reference
107
+ # @note This method was altered to avoid raising an exception by default.
108
+ # Instead, this method now returns `nil` in case of failure. For more info,
109
+ # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
110
+ # @return [Object] the new value, or nil if update failed
111
+ #
112
+ # @!method try_update!
113
+ # Pass the current value to the given block, replacing it
114
+ # with the block's result. Raise an exception if the update
115
+ # fails.
116
+ #
117
+ # @yield [Object] Calculate a new value for the atomic reference using
118
+ # given (old) value
119
+ # @yieldparam [Object] old_value the starting value of the atomic reference
120
+ # @note This behavior mimics the behavior of the original
121
+ # `AtomicReference#try_update` API. The reason this was changed was to
122
+ # avoid raising exceptions (which are inherently slow) by default. For more
123
+ # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
124
+ # @return [Object] the new value
125
+ # @raise [Concurrent::ConcurrentUpdateError] if the update fails
126
+ class AtomicReference < AtomicReferenceImplementation
127
+
128
+ # @return [String] Short string representation.
129
+ def to_s
130
+ format '%s value:%s>', super[0..-2], get
131
+ end
132
+
133
+ alias_method :inspect, :to_s
134
+ end
135
+ end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
  require 'concurrent/utility/native_integer'
3
3
 
4
4
  module Concurrent
@@ -1,5 +1,5 @@
1
1
  require 'thread'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -19,7 +19,7 @@ module Concurrent
19
19
  # t1 = Thread.new do
20
20
  # puts "t1 is waiting"
21
21
  # event.wait(1)
22
- # puts "event ocurred"
22
+ # puts "event occurred"
23
23
  # end
24
24
  #
25
25
  # t2 = Thread.new do
@@ -30,8 +30,8 @@ module Concurrent
30
30
  # [t1, t2].each(&:join)
31
31
  #
32
32
  # # prints:
33
- # # t2 calling set
34
33
  # # t1 is waiting
34
+ # # t2 calling set
35
35
  # # event occurred
36
36
  class Event < Synchronization::LockableObject
37
37
 
@@ -0,0 +1,109 @@
1
+ require 'concurrent/constants'
2
+ require_relative 'locals'
3
+
4
+ module Concurrent
5
+
6
+ # A `FiberLocalVar` is a variable where the value is different for each fiber.
7
+ # Each variable may have a default value, but when you modify the variable only
8
+ # the current fiber will ever see that change.
9
+ #
10
+ # This is similar to Ruby's built-in fiber-local variables (`Thread.current[:name]`),
11
+ # but with these major advantages:
12
+ # * `FiberLocalVar` has its own identity, it doesn't need a Symbol.
13
+ # * Each Ruby's built-in fiber-local variable leaks some memory forever (it's a Symbol held forever on the fiber),
14
+ # so it's only OK to create a small amount of them.
15
+ # `FiberLocalVar` has no such issue and it is fine to create many of them.
16
+ # * Ruby's built-in fiber-local variables leak forever the value set on each fiber (unless set to nil explicitly).
17
+ # `FiberLocalVar` automatically removes the mapping for each fiber once the `FiberLocalVar` instance is GC'd.
18
+ #
19
+ # @example
20
+ # v = FiberLocalVar.new(14)
21
+ # v.value #=> 14
22
+ # v.value = 2
23
+ # v.value #=> 2
24
+ #
25
+ # @example
26
+ # v = FiberLocalVar.new(14)
27
+ #
28
+ # Fiber.new do
29
+ # v.value #=> 14
30
+ # v.value = 1
31
+ # v.value #=> 1
32
+ # end.resume
33
+ #
34
+ # Fiber.new do
35
+ # v.value #=> 14
36
+ # v.value = 2
37
+ # v.value #=> 2
38
+ # end.resume
39
+ #
40
+ # v.value #=> 14
41
+ class FiberLocalVar
42
+ LOCALS = FiberLocals.new
43
+
44
+ # Creates a fiber local variable.
45
+ #
46
+ # @param [Object] default the default value when otherwise unset
47
+ # @param [Proc] default_block Optional block that gets called to obtain the
48
+ # default value for each fiber
49
+ def initialize(default = nil, &default_block)
50
+ if default && block_given?
51
+ raise ArgumentError, "Cannot use both value and block as default value"
52
+ end
53
+
54
+ if block_given?
55
+ @default_block = default_block
56
+ @default = nil
57
+ else
58
+ @default_block = nil
59
+ @default = default
60
+ end
61
+
62
+ @index = LOCALS.next_index(self)
63
+ end
64
+
65
+ # Returns the value in the current fiber's copy of this fiber-local variable.
66
+ #
67
+ # @return [Object] the current value
68
+ def value
69
+ LOCALS.fetch(@index) { default }
70
+ end
71
+
72
+ # Sets the current fiber's copy of this fiber-local variable to the specified value.
73
+ #
74
+ # @param [Object] value the value to set
75
+ # @return [Object] the new value
76
+ def value=(value)
77
+ LOCALS.set(@index, value)
78
+ end
79
+
80
+ # Bind the given value to fiber local storage during
81
+ # execution of the given block.
82
+ #
83
+ # @param [Object] value the value to bind
84
+ # @yield the operation to be performed with the bound variable
85
+ # @return [Object] the value
86
+ def bind(value)
87
+ if block_given?
88
+ old_value = self.value
89
+ self.value = value
90
+ begin
91
+ yield
92
+ ensure
93
+ self.value = old_value
94
+ end
95
+ end
96
+ end
97
+
98
+ protected
99
+
100
+ # @!visibility private
101
+ def default
102
+ if @default_block
103
+ self.value = @default_block.call
104
+ else
105
+ @default
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,4 +1,5 @@
1
1
  if Concurrent.on_jruby?
2
+ require 'concurrent/utility/native_extension_loader'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -0,0 +1,189 @@
1
+ require 'fiber'
2
+ require 'concurrent/utility/engine'
3
+ require 'concurrent/constants'
4
+
5
+ module Concurrent
6
+ # @!visibility private
7
+ # @!macro internal_implementation_note
8
+ #
9
+ # An abstract implementation of local storage, with sub-classes for
10
+ # per-thread and per-fiber locals.
11
+ #
12
+ # Each execution context (EC, thread or fiber) has a lazily initialized array
13
+ # of local variable values. Each time a new local variable is created, we
14
+ # allocate an "index" for it.
15
+ #
16
+ # For example, if the allocated index is 1, that means slot #1 in EVERY EC's
17
+ # locals array will be used for the value of that variable.
18
+ #
19
+ # The good thing about using a per-EC structure to hold values, rather than
20
+ # a global, is that no synchronization is needed when reading and writing
21
+ # those values (since the structure is only ever accessed by a single
22
+ # thread).
23
+ #
24
+ # Of course, when a local variable is GC'd, 1) we need to recover its index
25
+ # for use by other new local variables (otherwise the locals arrays could
26
+ # get bigger and bigger with time), and 2) we need to null out all the
27
+ # references held in the now-unused slots (both to avoid blocking GC of those
28
+ # objects, and also to prevent "stale" values from being passed on to a new
29
+ # local when the index is reused).
30
+ #
31
+ # Because we need to null out freed slots, we need to keep references to
32
+ # ALL the locals arrays, so we can null out the appropriate slots in all of
33
+ # them. This is why we need to use a finalizer to clean up the locals array
34
+ # when the EC goes out of scope.
35
+ class AbstractLocals
36
+ def initialize
37
+ @free = []
38
+ @lock = Mutex.new
39
+ @all_arrays = {}
40
+ @next = 0
41
+ end
42
+
43
+ def synchronize
44
+ @lock.synchronize { yield }
45
+ end
46
+
47
+ if Concurrent.on_cruby?
48
+ def weak_synchronize
49
+ yield
50
+ end
51
+ else
52
+ alias_method :weak_synchronize, :synchronize
53
+ end
54
+
55
+ def next_index(local)
56
+ index = synchronize do
57
+ if @free.empty?
58
+ @next += 1
59
+ else
60
+ @free.pop
61
+ end
62
+ end
63
+
64
+ # When the local goes out of scope, we should free the associated index
65
+ # and all values stored into it.
66
+ ObjectSpace.define_finalizer(local, local_finalizer(index))
67
+
68
+ index
69
+ end
70
+
71
+ def free_index(index)
72
+ weak_synchronize do
73
+ # The cost of GC'ing a TLV is linear in the number of ECs using local
74
+ # variables. But that is natural! More ECs means more storage is used
75
+ # per local variable. So naturally more CPU time is required to free
76
+ # more storage.
77
+ #
78
+ # DO NOT use each_value which might conflict with new pair assignment
79
+ # into the hash in #set method.
80
+ @all_arrays.values.each do |locals|
81
+ locals[index] = nil
82
+ end
83
+
84
+ # free index has to be published after the arrays are cleared:
85
+ @free << index
86
+ end
87
+ end
88
+
89
+ def fetch(index)
90
+ locals = self.locals
91
+ value = locals ? locals[index] : nil
92
+
93
+ if nil == value
94
+ yield
95
+ elsif NULL.equal?(value)
96
+ nil
97
+ else
98
+ value
99
+ end
100
+ end
101
+
102
+ def set(index, value)
103
+ locals = self.locals!
104
+ locals[index] = (nil == value ? NULL : value)
105
+
106
+ value
107
+ end
108
+
109
+ private
110
+
111
+ # When the local goes out of scope, clean up that slot across all locals currently assigned.
112
+ def local_finalizer(index)
113
+ proc do
114
+ free_index(index)
115
+ end
116
+ end
117
+
118
+ # When a thread/fiber goes out of scope, remove the array from @all_arrays.
119
+ def thread_fiber_finalizer(array_object_id)
120
+ proc do
121
+ weak_synchronize do
122
+ @all_arrays.delete(array_object_id)
123
+ end
124
+ end
125
+ end
126
+
127
+ # Returns the locals for the current scope, or nil if none exist.
128
+ def locals
129
+ raise NotImplementedError
130
+ end
131
+
132
+ # Returns the locals for the current scope, creating them if necessary.
133
+ def locals!
134
+ raise NotImplementedError
135
+ end
136
+ end
137
+
138
+ # @!visibility private
139
+ # @!macro internal_implementation_note
140
+ # An array-backed storage of indexed variables per thread.
141
+ class ThreadLocals < AbstractLocals
142
+ def locals
143
+ Thread.current.thread_variable_get(:concurrent_thread_locals)
144
+ end
145
+
146
+ def locals!
147
+ thread = Thread.current
148
+ locals = thread.thread_variable_get(:concurrent_thread_locals)
149
+
150
+ unless locals
151
+ locals = thread.thread_variable_set(:concurrent_thread_locals, [])
152
+ weak_synchronize do
153
+ @all_arrays[locals.object_id] = locals
154
+ end
155
+ # When the thread goes out of scope, we should delete the associated locals:
156
+ ObjectSpace.define_finalizer(thread, thread_fiber_finalizer(locals.object_id))
157
+ end
158
+
159
+ locals
160
+ end
161
+ end
162
+
163
+ # @!visibility private
164
+ # @!macro internal_implementation_note
165
+ # An array-backed storage of indexed variables per fiber.
166
+ class FiberLocals < AbstractLocals
167
+ def locals
168
+ Thread.current[:concurrent_fiber_locals]
169
+ end
170
+
171
+ def locals!
172
+ thread = Thread.current
173
+ locals = thread[:concurrent_fiber_locals]
174
+
175
+ unless locals
176
+ locals = thread[:concurrent_fiber_locals] = []
177
+ weak_synchronize do
178
+ @all_arrays[locals.object_id] = locals
179
+ end
180
+ # When the fiber goes out of scope, we should delete the associated locals:
181
+ ObjectSpace.define_finalizer(Fiber.current, thread_fiber_finalizer(locals.object_id))
182
+ end
183
+
184
+ locals
185
+ end
186
+ end
187
+
188
+ private_constant :AbstractLocals, :ThreadLocals, :FiberLocals
189
+ end
@@ -0,0 +1,28 @@
1
+ require 'concurrent/utility/engine'
2
+ require_relative 'fiber_local_var'
3
+ require_relative 'thread_local_var'
4
+
5
+ module Concurrent
6
+ # @!visibility private
7
+ def self.mutex_owned_per_thread?
8
+ return false if Concurrent.on_jruby? || Concurrent.on_truffleruby?
9
+
10
+ mutex = Mutex.new
11
+ # Lock the mutex:
12
+ mutex.synchronize do
13
+ # Check if the mutex is still owned in a child fiber:
14
+ Fiber.new { mutex.owned? }.resume
15
+ end
16
+ end
17
+
18
+ if mutex_owned_per_thread?
19
+ LockLocalVar = ThreadLocalVar
20
+ else
21
+ LockLocalVar = FiberLocalVar
22
+ end
23
+
24
+ # Either {FiberLocalVar} or {ThreadLocalVar} depending on whether Mutex (and Monitor)
25
+ # are held, respectively, per Fiber or per Thread.
26
+ class LockLocalVar
27
+ end
28
+ end
@@ -1,16 +1,18 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/safe_initialization'
2
2
 
3
3
  module Concurrent
4
4
 
5
5
  # @!macro atomic_boolean
6
6
  # @!visibility private
7
7
  # @!macro internal_implementation_note
8
- class MutexAtomicBoolean < Synchronization::LockableObject
8
+ class MutexAtomicBoolean
9
+ extend Concurrent::Synchronization::SafeInitialization
9
10
 
10
11
  # @!macro atomic_boolean_method_initialize
11
12
  def initialize(initial = false)
12
13
  super()
13
- synchronize { ns_initialize(initial) }
14
+ @Lock = ::Mutex.new
15
+ @value = !!initial
14
16
  end
15
17
 
16
18
  # @!macro atomic_boolean_method_value_get
@@ -46,8 +48,12 @@ module Concurrent
46
48
  protected
47
49
 
48
50
  # @!visibility private
49
- def ns_initialize(initial)
50
- @value = !!initial
51
+ def synchronize
52
+ if @Lock.owned?
53
+ yield
54
+ else
55
+ @Lock.synchronize { yield }
56
+ end
51
57
  end
52
58
 
53
59
  private
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/safe_initialization'
2
2
  require 'concurrent/utility/native_integer'
3
3
 
4
4
  module Concurrent
@@ -6,12 +6,14 @@ module Concurrent
6
6
  # @!macro atomic_fixnum
7
7
  # @!visibility private
8
8
  # @!macro internal_implementation_note
9
- class MutexAtomicFixnum < Synchronization::LockableObject
9
+ class MutexAtomicFixnum
10
+ extend Concurrent::Synchronization::SafeInitialization
10
11
 
11
12
  # @!macro atomic_fixnum_method_initialize
12
13
  def initialize(initial = 0)
13
14
  super()
14
- synchronize { ns_initialize(initial) }
15
+ @Lock = ::Mutex.new
16
+ ns_set(initial)
15
17
  end
16
18
 
17
19
  # @!macro atomic_fixnum_method_value_get
@@ -60,8 +62,12 @@ module Concurrent
60
62
  protected
61
63
 
62
64
  # @!visibility private
63
- def ns_initialize(initial)
64
- ns_set(initial)
65
+ def synchronize
66
+ if @Lock.owned?
67
+ yield
68
+ else
69
+ @Lock.synchronize { yield }
70
+ end
65
71
  end
66
72
 
67
73
  private
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
  require 'concurrent/utility/native_integer'
3
3
 
4
4
  module Concurrent