concurrent-ruby 1.1.9 → 1.2.0

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile +2 -8
  4. data/README.md +34 -37
  5. data/Rakefile +48 -69
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  47. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  48. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  49. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  51. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +9 -9
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  78. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  79. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  80. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  81. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  82. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  83. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  84. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  85. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  86. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  87. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  88. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  89. metadata +10 -12
  90. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  91. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  92. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  93. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  94. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  96. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  97. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -1,6 +1,5 @@
1
1
  require 'thread'
2
2
  require 'concurrent/constants'
3
- require 'concurrent/synchronization'
4
3
  require 'concurrent/utility/engine'
5
4
 
6
5
  module Concurrent
@@ -10,17 +9,20 @@ module Concurrent
10
9
  # @!visibility private
11
10
  MapImplementation = case
12
11
  when Concurrent.on_jruby?
12
+ require 'concurrent/utility/native_extension_loader'
13
13
  # noinspection RubyResolve
14
14
  JRubyMapBackend
15
15
  when Concurrent.on_cruby?
16
16
  require 'concurrent/collection/map/mri_map_backend'
17
17
  MriMapBackend
18
- when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap)
19
- require 'concurrent/collection/map/truffleruby_map_backend'
20
- TruffleRubyMapBackend
21
- when Concurrent.on_truffleruby? || Concurrent.on_rbx?
22
- require 'concurrent/collection/map/atomic_reference_map_backend'
23
- AtomicReferenceMapBackend
18
+ when Concurrent.on_truffleruby?
19
+ if defined?(::TruffleRuby::ConcurrentMap)
20
+ require 'concurrent/collection/map/truffleruby_map_backend'
21
+ TruffleRubyMapBackend
22
+ else
23
+ require 'concurrent/collection/map/atomic_reference_map_backend'
24
+ AtomicReferenceMapBackend
25
+ end
24
26
  else
25
27
  warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
26
28
  require 'concurrent/collection/map/synchronized_map_backend'
@@ -197,7 +199,6 @@ module Concurrent
197
199
  # @yieldparam key [Object]
198
200
  # @yieldreturn [Object] default value
199
201
  # @return [Object] the value or default value
200
- # @!macro map.atomic_method_with_block
201
202
  def fetch_or_store(key, default_value = NULL)
202
203
  fetch(key) do
203
204
  put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
@@ -281,7 +282,6 @@ module Concurrent
281
282
  each_pair { |k, v| return k if v == value }
282
283
  nil
283
284
  end unless method_defined?(:key)
284
- alias_method :index, :key if RUBY_VERSION < '1.9'
285
285
 
286
286
  # Is map empty?
287
287
  # @return [true, false]
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/concern/dereferenceable'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -66,7 +66,7 @@ module Concurrent
66
66
  # Start by requiring promises
67
67
  #
68
68
  # ```ruby
69
- # require 'concurrent'
69
+ # require 'concurrent/promise'
70
70
  # ```
71
71
  #
72
72
  # Then create one
@@ -1,7 +1,8 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
  require 'concurrent/atomic/atomic_boolean'
3
3
  require 'concurrent/atomic/atomic_fixnum'
4
4
  require 'concurrent/collection/lock_free_stack'
5
+ require 'concurrent/configuration'
5
6
  require 'concurrent/errors'
6
7
  require 'concurrent/re_include'
7
8
 
@@ -313,7 +314,7 @@ module Concurrent
313
314
  end
314
315
 
315
316
  # @!macro promises.shortcut.on
316
- # @return [Future]
317
+ # @return [Event]
317
318
  def any_event(*futures_and_or_events)
318
319
  any_event_on default_executor, *futures_and_or_events
319
320
  end
@@ -892,7 +893,7 @@ module Concurrent
892
893
  private
893
894
 
894
895
  def rejected_resolution(raise_on_reassign, state)
895
- Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
+ raise Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
897
  return false
897
898
  end
898
899
 
@@ -1298,7 +1299,7 @@ module Concurrent
1298
1299
 
1299
1300
  # @!macro promise.param.raise_on_reassign
1300
1301
  # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1301
- # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1302
+ # @return [self, false] false is returned when raise_on_reassign is false and the receiver
1302
1303
  # is already resolved.
1303
1304
  #
1304
1305
 
@@ -2074,8 +2075,8 @@ module Concurrent
2074
2075
 
2075
2076
  private
2076
2077
 
2077
- def resolvable?(countdown, future, index)
2078
- future.fulfilled? ||
2078
+ def resolvable?(countdown, event_or_future, index)
2079
+ (event_or_future.is_a?(Event) ? event_or_future.resolved? : event_or_future.fulfilled?) ||
2079
2080
  # inlined super from BlockedPromise
2080
2081
  countdown.zero?
2081
2082
  end
@@ -31,6 +31,8 @@ module Concurrent
31
31
  #
32
32
  # C1.new.respond_to? :a # => false
33
33
  # C2.new.respond_to? :a # => true
34
+ #
35
+ # @!visibility private
34
36
  module ReInclude
35
37
  # @!visibility private
36
38
  def included(base)
@@ -57,30 +57,43 @@ module Concurrent
57
57
  #
58
58
  # @example Basic usage
59
59
  #
60
- # require 'concurrent'
61
- # require 'thread' # for Queue
62
- # require 'open-uri' # for open(uri)
60
+ # require 'concurrent/scheduled_task'
61
+ # require 'csv'
62
+ # require 'open-uri'
63
63
  #
64
64
  # class Ticker
65
- # def get_year_end_closing(symbol, year)
66
- # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
67
- # data = open(uri) {|f| f.collect{|line| line.strip } }
68
- # data[1].split(',')[4].to_f
69
- # end
65
+ # def get_year_end_closing(symbol, year, api_key)
66
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
67
+ # data = []
68
+ # csv = URI.parse(uri).read
69
+ # if csv.include?('call frequency')
70
+ # return :rate_limit_exceeded
71
+ # end
72
+ # CSV.parse(csv, headers: true) do |row|
73
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
74
+ # end
75
+ # year_end = data.first
76
+ # year_end
77
+ # rescue => e
78
+ # p e
79
+ # end
70
80
  # end
71
81
  #
82
+ # api_key = ENV['ALPHAVANTAGE_KEY']
83
+ # abort(error_message) unless api_key
84
+ #
72
85
  # # Future
73
- # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
86
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
74
87
  # price.state #=> :pending
75
- # sleep(1) # do other stuff
76
- # price.value #=> 63.65
77
- # price.state #=> :fulfilled
88
+ # price.pending? #=> true
89
+ # price.value(0) #=> nil (does not block)
78
90
  #
79
- # # ScheduledTask
80
- # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
81
- # task.state #=> :pending
82
- # sleep(3) # do other stuff
83
- # task.value #=> 25.96
91
+ # sleep(1) # do other stuff
92
+ #
93
+ # price.value #=> 63.65 (after blocking if necessary)
94
+ # price.state #=> :fulfilled
95
+ # price.fulfilled? #=> true
96
+ # price.value #=> 63.65
84
97
  #
85
98
  # @example Successful task execution
86
99
  #
@@ -42,16 +42,6 @@ module Concurrent
42
42
 
43
43
  JRubySet
44
44
 
45
- when Concurrent.on_rbx?
46
- require 'monitor'
47
- require 'concurrent/thread_safe/util/data_structures'
48
-
49
- class RbxSet < ::Set
50
- end
51
-
52
- ThreadSafe::Util.make_synchronized_on_rbx RbxSet
53
- RbxSet
54
-
55
45
  when Concurrent.on_truffleruby?
56
46
  require 'concurrent/thread_safe/util/data_structures'
57
47
 
@@ -1,6 +1,6 @@
1
- require 'concurrent/synchronization/abstract_struct'
2
1
  require 'concurrent/errors'
3
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/abstract_struct'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -1,3 +1,7 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+ require 'concurrent/utility/monotonic_time'
3
+ require 'concurrent/synchronization/object'
4
+
1
5
  module Concurrent
2
6
  module Synchronization
3
7
 
@@ -34,7 +38,7 @@ module Concurrent
34
38
  if timeout
35
39
  wait_until = Concurrent.monotonic_time + timeout
36
40
  loop do
37
- now = Concurrent.monotonic_time
41
+ now = Concurrent.monotonic_time
38
42
  condition_result = condition.call
39
43
  return condition_result if now >= wait_until || condition_result
40
44
  ns_wait wait_until - now
@@ -4,10 +4,8 @@ module Concurrent
4
4
  # @!visibility private
5
5
  # @!macro internal_implementation_note
6
6
  class AbstractObject
7
-
8
- # @abstract has to be implemented based on Ruby runtime
9
7
  def initialize
10
- raise NotImplementedError
8
+ # nothing to do
11
9
  end
12
10
 
13
11
  # @!visibility private
@@ -1,3 +1,5 @@
1
+ require 'concurrent/synchronization/lockable_object'
2
+
1
3
  module Concurrent
2
4
  module Synchronization
3
5
 
@@ -0,0 +1,29 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
3
+ module Concurrent
4
+ module Synchronization
5
+ case
6
+ when Concurrent.on_cruby?
7
+ def self.full_memory_barrier
8
+ # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
9
+ # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
10
+ end
11
+
12
+ when Concurrent.on_jruby?
13
+ require 'concurrent/utility/native_extension_loader'
14
+ def self.full_memory_barrier
15
+ JRubyAttrVolatile.full_memory_barrier
16
+ end
17
+
18
+ when Concurrent.on_truffleruby?
19
+ def self.full_memory_barrier
20
+ TruffleRuby.full_memory_barrier
21
+ end
22
+
23
+ else
24
+ warn 'Possibly unsupported Ruby implementation'
25
+ def self.full_memory_barrier
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,7 +1,9 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
1
3
  module Concurrent
2
4
  module Synchronization
3
5
 
4
- if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
6
+ if Concurrent.on_jruby?
5
7
 
6
8
  # @!visibility private
7
9
  # @!macro internal_implementation_note
@@ -1,3 +1,5 @@
1
+ require 'concurrent/synchronization/lockable_object'
2
+
1
3
  module Concurrent
2
4
  module Synchronization
3
5
 
@@ -1,17 +1,18 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/synchronization/abstract_lockable_object'
3
+ require 'concurrent/synchronization/mutex_lockable_object'
4
+ require 'concurrent/synchronization/jruby_lockable_object'
5
+
1
6
  module Concurrent
2
7
  module Synchronization
3
8
 
4
9
  # @!visibility private
5
10
  # @!macro internal_implementation_note
6
11
  LockableObjectImplementation = case
7
- when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
8
- MonitorLockableObject
9
- when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
12
+ when Concurrent.on_cruby?
10
13
  MutexLockableObject
11
14
  when Concurrent.on_jruby?
12
15
  JRubyLockableObject
13
- when Concurrent.on_rbx?
14
- RbxLockableObject
15
16
  when Concurrent.on_truffleruby?
16
17
  MutexLockableObject
17
18
  else
@@ -1,5 +1,6 @@
1
+ require 'concurrent/synchronization/abstract_lockable_object'
2
+
1
3
  module Concurrent
2
- # noinspection RubyInstanceVariableNamingConvention
3
4
  module Synchronization
4
5
 
5
6
  # @!visibility private
@@ -26,8 +27,8 @@ module Concurrent
26
27
 
27
28
  safe_initialization!
28
29
 
29
- def initialize(*defaults)
30
- super(*defaults)
30
+ def initialize
31
+ super()
31
32
  @__Lock__ = ::Mutex.new
32
33
  @__Condition__ = ::ConditionVariable.new
33
34
  end
@@ -61,8 +62,8 @@ module Concurrent
61
62
 
62
63
  safe_initialization!
63
64
 
64
- def initialize(*defaults)
65
- super(*defaults)
65
+ def initialize
66
+ super()
66
67
  @__Lock__ = ::Monitor.new
67
68
  @__Condition__ = @__Lock__.new_cond
68
69
  end
@@ -1,28 +1,20 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
3
+ require 'concurrent/synchronization/safe_initialization'
4
+ require 'concurrent/synchronization/volatile'
5
+ require 'concurrent/atomic/atomic_reference'
6
+
1
7
  module Concurrent
2
8
  module Synchronization
3
9
 
4
- # @!visibility private
5
- # @!macro internal_implementation_note
6
- ObjectImplementation = case
7
- when Concurrent.on_cruby?
8
- MriObject
9
- when Concurrent.on_jruby?
10
- JRubyObject
11
- when Concurrent.on_rbx?
12
- RbxObject
13
- when Concurrent.on_truffleruby?
14
- TruffleRubyObject
15
- else
16
- warn 'Possibly unsupported Ruby implementation'
17
- MriObject
18
- end
19
- private_constant :ObjectImplementation
20
-
21
10
  # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
22
11
  # - final instance variables see {Object.safe_initialization!}
23
12
  # - volatile instance variables see {Object.attr_volatile}
24
13
  # - volatile instance variables see {Object.attr_atomic}
25
- class Object < ObjectImplementation
14
+ # @!visibility private
15
+ class Object < AbstractObject
16
+ include Volatile
17
+
26
18
  # TODO make it a module if possible
27
19
 
28
20
  # @!method self.attr_volatile(*names)
@@ -38,36 +30,12 @@ module Concurrent
38
30
  __initialize_atomic_fields__
39
31
  end
40
32
 
41
- # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
42
- # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
43
- # same behaviour as Java's final fields.
44
- # @example
45
- # class AClass < Concurrent::Synchronization::Object
46
- # safe_initialization!
47
- #
48
- # def initialize
49
- # @AFinalValue = 'value' # published safely, does not have to be synchronized
50
- # end
51
- # end
52
- # @return [true]
53
33
  def self.safe_initialization!
54
- # define only once, and not again in children
55
- return if safe_initialization?
56
-
57
- # @!visibility private
58
- def self.new(*args, &block)
59
- object = super(*args, &block)
60
- ensure
61
- object.full_memory_barrier if object
62
- end
63
-
64
- @safe_initialization = true
34
+ extend SafeInitialization unless safe_initialization?
65
35
  end
66
36
 
67
- # @return [true, false] if this class is safely initialized.
68
37
  def self.safe_initialization?
69
- @safe_initialization = false unless defined? @safe_initialization
70
- @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
38
+ self.singleton_class < SafeInitialization
71
39
  end
72
40
 
73
41
  # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
@@ -0,0 +1,36 @@
1
+ require 'concurrent/synchronization/full_memory_barrier'
2
+
3
+ module Concurrent
4
+ module Synchronization
5
+
6
+ # @!visibility private
7
+ # @!macro internal_implementation_note
8
+ #
9
+ # By extending this module, a class and all its children are marked to be constructed safely. Meaning that
10
+ # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
11
+ # same behaviour as Java's final fields.
12
+ #
13
+ # Due to using Kernel#extend, the module is not included again if already present in the ancestors,
14
+ # which avoids extra overhead.
15
+ #
16
+ # @example
17
+ # class AClass < Concurrent::Synchronization::Object
18
+ # extend Concurrent::Synchronization::SafeInitialization
19
+ #
20
+ # def initialize
21
+ # @AFinalValue = 'value' # published safely, #foo will never return nil
22
+ # end
23
+ #
24
+ # def foo
25
+ # @AFinalValue
26
+ # end
27
+ # end
28
+ module SafeInitialization
29
+ def new(*args, &block)
30
+ super(*args, &block)
31
+ ensure
32
+ Concurrent::Synchronization.full_memory_barrier
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,7 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+ require 'concurrent/utility/engine'
3
+ require 'concurrent/synchronization/full_memory_barrier'
4
+
1
5
  module Concurrent
2
6
  module Synchronization
3
7
 
@@ -19,18 +23,79 @@ module Concurrent
19
23
  # => 1
20
24
  # foo.bar = 2
21
25
  # => 2
26
+ #
27
+ # @!visibility private
28
+ module Volatile
29
+ def self.included(base)
30
+ base.extend(ClassMethods)
31
+ end
32
+
33
+ def full_memory_barrier
34
+ Synchronization.full_memory_barrier
35
+ end
36
+
37
+ module ClassMethods
38
+ if Concurrent.on_cruby?
39
+ def attr_volatile(*names)
40
+ names.each do |name|
41
+ ivar = :"@volatile_#{name}"
42
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
43
+ def #{name}
44
+ #{ivar}
45
+ end
46
+
47
+ def #{name}=(value)
48
+ #{ivar} = value
49
+ end
50
+ RUBY
51
+ end
52
+ names.map { |n| [n, :"#{n}="] }.flatten
53
+ end
54
+
55
+ elsif Concurrent.on_jruby?
56
+ def attr_volatile(*names)
57
+ names.each do |name|
58
+ ivar = :"@volatile_#{name}"
59
+
60
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
+ def #{name}
62
+ ::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_get_volatile(self, :#{ivar})
63
+ end
64
+
65
+ def #{name}=(value)
66
+ ::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_set_volatile(self, :#{ivar}, value)
67
+ end
68
+ RUBY
69
+
70
+ end
71
+ names.map { |n| [n, :"#{n}="] }.flatten
72
+ end
73
+
74
+ else
75
+ warn 'Possibly unsupported Ruby implementation' unless Concurrent.on_truffleruby?
76
+
77
+ def attr_volatile(*names)
78
+ names.each do |name|
79
+ ivar = :"@volatile_#{name}"
80
+
81
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
82
+ def #{name}
83
+ ::Concurrent::Synchronization.full_memory_barrier
84
+ #{ivar}
85
+ end
86
+
87
+ def #{name}=(value)
88
+ #{ivar} = value
89
+ ::Concurrent::Synchronization.full_memory_barrier
90
+ end
91
+ RUBY
92
+ end
93
+
94
+ names.map { |n| [n, :"#{n}="] }.flatten
95
+ end
96
+ end
97
+ end
22
98
 
23
- Volatile = case
24
- when Concurrent.on_cruby?
25
- MriAttrVolatile
26
- when Concurrent.on_jruby?
27
- JRubyAttrVolatile
28
- when Concurrent.on_rbx?
29
- RbxAttrVolatile
30
- when Concurrent.on_truffleruby?
31
- TruffleRubyAttrVolatile
32
- else
33
- MriAttrVolatile
34
- end
99
+ end
35
100
  end
36
101
  end
@@ -1,29 +1,12 @@
1
- require 'concurrent/utility/engine'
2
-
3
- require 'concurrent/synchronization/abstract_object'
4
1
  require 'concurrent/utility/native_extension_loader' # load native parts first
5
- Concurrent.load_native_extensions
6
2
 
7
- require 'concurrent/synchronization/mri_object'
8
- require 'concurrent/synchronization/jruby_object'
9
- require 'concurrent/synchronization/rbx_object'
10
- require 'concurrent/synchronization/truffleruby_object'
11
3
  require 'concurrent/synchronization/object'
12
- require 'concurrent/synchronization/volatile'
13
-
14
- require 'concurrent/synchronization/abstract_lockable_object'
15
- require 'concurrent/synchronization/mutex_lockable_object'
16
- require 'concurrent/synchronization/jruby_lockable_object'
17
- require 'concurrent/synchronization/rbx_lockable_object'
18
-
19
4
  require 'concurrent/synchronization/lockable_object'
20
-
21
5
  require 'concurrent/synchronization/condition'
22
6
  require 'concurrent/synchronization/lock'
23
7
 
24
8
  module Concurrent
25
- # {include:file:docs-source/synchronization.md}
26
- # {include:file:docs-source/synchronization-notes.md}
9
+ # @!visibility private
27
10
  module Synchronization
28
11
  end
29
12
  end