concurrent-ruby 0.9.2-java → 1.0.0-java

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -1
  3. data/README.md +86 -120
  4. data/lib/concurrent.rb +14 -5
  5. data/lib/concurrent/agent.rb +587 -0
  6. data/lib/concurrent/array.rb +39 -0
  7. data/lib/concurrent/async.rb +296 -149
  8. data/lib/concurrent/atom.rb +135 -45
  9. data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
  10. data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
  11. data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
  12. data/lib/concurrent/atomic/atomic_reference.rb +1 -8
  13. data/lib/concurrent/atomic/count_down_latch.rb +62 -103
  14. data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
  15. data/lib/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
  17. data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
  18. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
  19. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
  20. data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
  21. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  22. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  23. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  24. data/lib/concurrent/atomic/ruby_thread_local_var.rb +172 -0
  25. data/lib/concurrent/atomic/semaphore.rb +84 -178
  26. data/lib/concurrent/atomic/thread_local_var.rb +65 -294
  27. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  28. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  29. data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
  30. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  31. data/lib/concurrent/atomics.rb +7 -37
  32. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  33. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  34. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  35. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  36. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  37. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
  38. data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
  39. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  40. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  41. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  42. data/lib/concurrent/concern/logging.rb +1 -1
  43. data/lib/concurrent/concern/obligation.rb +11 -20
  44. data/lib/concurrent/concern/observable.rb +38 -13
  45. data/lib/concurrent/configuration.rb +23 -152
  46. data/lib/concurrent/constants.rb +8 -0
  47. data/lib/concurrent/delay.rb +14 -12
  48. data/lib/concurrent/exchanger.rb +339 -41
  49. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  50. data/lib/concurrent/executor/executor_service.rb +23 -359
  51. data/lib/concurrent/executor/immediate_executor.rb +3 -2
  52. data/lib/concurrent/executor/java_executor_service.rb +100 -0
  53. data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
  54. data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
  55. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  56. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
  57. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
  58. data/lib/concurrent/executor/safe_task_executor.rb +6 -7
  59. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  60. data/lib/concurrent/executor/serialized_execution.rb +10 -33
  61. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  62. data/lib/concurrent/executor/simple_executor_service.rb +1 -10
  63. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  64. data/lib/concurrent/executor/timer_set.rb +8 -10
  65. data/lib/concurrent/executors.rb +12 -2
  66. data/lib/concurrent/future.rb +6 -4
  67. data/lib/concurrent/hash.rb +35 -0
  68. data/lib/concurrent/immutable_struct.rb +5 -1
  69. data/lib/concurrent/ivar.rb +12 -16
  70. data/lib/concurrent/lazy_register.rb +11 -8
  71. data/lib/concurrent/map.rb +180 -0
  72. data/lib/concurrent/maybe.rb +6 -3
  73. data/lib/concurrent/mutable_struct.rb +7 -6
  74. data/lib/concurrent/mvar.rb +26 -2
  75. data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
  76. data/lib/concurrent/promise.rb +7 -5
  77. data/lib/concurrent/scheduled_task.rb +13 -71
  78. data/lib/concurrent/settable_struct.rb +5 -4
  79. data/lib/concurrent/synchronization.rb +15 -3
  80. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  81. data/lib/concurrent/synchronization/abstract_object.rb +7 -146
  82. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  83. data/lib/concurrent/synchronization/condition.rb +6 -4
  84. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  85. data/lib/concurrent/synchronization/jruby_object.rb +44 -0
  86. data/lib/concurrent/synchronization/lock.rb +3 -2
  87. data/lib/concurrent/synchronization/lockable_object.rb +72 -0
  88. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  89. data/lib/concurrent/synchronization/mri_object.rb +43 -0
  90. data/lib/concurrent/synchronization/object.rb +140 -73
  91. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  92. data/lib/concurrent/synchronization/rbx_object.rb +30 -73
  93. data/lib/concurrent/synchronization/volatile.rb +34 -0
  94. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  95. data/lib/concurrent/thread_safe/util.rb +14 -0
  96. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  97. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
  98. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  99. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  100. data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
  101. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  102. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  103. data/lib/concurrent/timer_task.rb +3 -4
  104. data/lib/concurrent/tuple.rb +86 -0
  105. data/lib/concurrent/tvar.rb +5 -1
  106. data/lib/concurrent/utility/at_exit.rb +1 -1
  107. data/lib/concurrent/utility/engine.rb +4 -0
  108. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  109. data/lib/concurrent/utility/native_extension_loader.rb +50 -30
  110. data/lib/concurrent/version.rb +2 -2
  111. data/lib/concurrent_ruby_ext.jar +0 -0
  112. metadata +47 -12
  113. data/lib/concurrent/atomic/condition.rb +0 -78
  114. data/lib/concurrent/collection/priority_queue.rb +0 -360
  115. data/lib/concurrent/synchronization/java_object.rb +0 -34
  116. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  117. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
  118. data/lib/concurrent/utilities.rb +0 -5
  119. data/lib/concurrent/utility/timeout.rb +0 -39
  120. data/lib/concurrent/utility/timer.rb +0 -26
  121. data/lib/concurrent_ruby.rb +0 -2
@@ -9,7 +9,6 @@ module Concurrent
9
9
  def initialize(*values)
10
10
  super()
11
11
  ns_initialize(*values)
12
- ensure_ivar_visibility!
13
12
  end
14
13
 
15
14
  # @!macro [attach] struct_length
@@ -131,7 +130,7 @@ module Concurrent
131
130
  def self.define_struct_class(parent, base, name, members, &block)
132
131
  clazz = Class.new(base || Object) do
133
132
  include parent
134
- self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
133
+ self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
135
134
  def ns_initialize(*values)
136
135
  raise ArgumentError.new('struct size differs') if values.length > length
137
136
  @values = values.fill(nil, values.length..length-1)
@@ -142,7 +141,7 @@ module Concurrent
142
141
  parent.const_set(name, clazz)
143
142
  parent.const_get(name)
144
143
  rescue NameError
145
- raise NameError.new("identifier #{name} needs to be constant")
144
+ raise NameError.new("identifier #{name} needs to be constant")
146
145
  end
147
146
  end
148
147
  members.each_with_index do |member, index|
@@ -1,14 +1,16 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
- class Condition < Object
3
+ class Condition < LockableObject
4
+ safe_initialization!
5
+
6
+ # TODO (pitr 12-Sep-2015): locks two objects, improve
4
7
 
5
8
  singleton_class.send :alias_method, :private_new, :new
6
9
  private_class_method :new
7
10
 
8
11
  def initialize(lock)
9
- @Lock = lock
10
- ensure_ivar_visibility!
11
12
  super()
13
+ @Lock = lock
12
14
  end
13
15
 
14
16
  def wait(timeout = nil)
@@ -44,7 +46,7 @@ module Concurrent
44
46
  end
45
47
  end
46
48
 
47
- class Object < Implementation
49
+ class LockableObject < LockableObjectImplementation
48
50
  def new_condition
49
51
  Condition.private_new(self)
50
52
  end
@@ -0,0 +1,13 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
5
+
6
+ # @!visibility private
7
+ # @!macro internal_implementation_note
8
+ class JRubyLockableObject < AbstractLockableObject
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
5
+
6
+ module JRubyAttrVolatile
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def attr_volatile(*names)
13
+ names.each do |name|
14
+
15
+ ivar = :"@volatile_#{name}"
16
+
17
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{name}
19
+ instance_variable_get_volatile(:#{ivar})
20
+ end
21
+
22
+ def #{name}=(value)
23
+ instance_variable_set_volatile(:#{ivar}, value)
24
+ end
25
+ RUBY
26
+
27
+ end
28
+ names.map { |n| [n, :"#{n}="] }.flatten
29
+ end
30
+ end
31
+ end
32
+
33
+ # @!visibility private
34
+ # @!macro internal_implementation_note
35
+ class JRubyObject < AbstractObject
36
+ include JRubyAttrVolatile
37
+
38
+ def initialize
39
+ # nothing to do
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,6 +1,7 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
- class Lock < Object
3
+ class Lock < LockableObject
4
+ # TODO use JavaReentrantLock on JRuby
4
5
 
5
6
  public :synchronize
6
7
 
@@ -21,7 +22,7 @@ module Concurrent
21
22
  end
22
23
 
23
24
  public :ns_signal
24
-
25
+
25
26
  def broadcast
26
27
  synchronize { ns_broadcast }
27
28
  end
@@ -0,0 +1,72 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ LockableObjectImplementation = case
7
+ when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
8
+ MriMonitorLockableObject
9
+ when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
10
+ MriMutexLockableObject
11
+ when Concurrent.on_jruby?
12
+ JRubyLockableObject
13
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
14
+ RbxLockableObject
15
+ else
16
+ warn 'Possibly unsupported Ruby implementation'
17
+ MriMonitorLockableObject
18
+ end
19
+ private_constant :LockableObjectImplementation
20
+
21
+ # Safe synchronization under any Ruby implementation.
22
+ # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
23
+ # Provides a single layer which can improve its implementation over time without changes needed to
24
+ # the classes using it. Use {Synchronization::Object} not this abstract class.
25
+ #
26
+ # @note this object does not support usage together with
27
+ # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
28
+ # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
29
+ # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
30
+ # `Thread#wakeup` will not work on all platforms.
31
+ #
32
+ # @see {Event} implementation as an example of this class use
33
+ #
34
+ # @example simple
35
+ # class AnClass < Synchronization::Object
36
+ # def initialize
37
+ # super
38
+ # synchronize { @value = 'asd' }
39
+ # end
40
+ #
41
+ # def value
42
+ # synchronize { @value }
43
+ # end
44
+ # end
45
+ #
46
+ # @!visibility private
47
+ class LockableObject < LockableObjectImplementation
48
+
49
+ # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
50
+ # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
51
+
52
+ # @!method initialize(*args, &block)
53
+ # @!macro synchronization_object_method_initialize
54
+
55
+ # @!method synchronize
56
+ # @!macro synchronization_object_method_synchronize
57
+
58
+ # @!method wait_until(timeout = nil, &condition)
59
+ # @!macro synchronization_object_method_ns_wait_until
60
+
61
+ # @!method wait(timeout = nil)
62
+ # @!macro synchronization_object_method_ns_wait
63
+
64
+ # @!method signal
65
+ # @!macro synchronization_object_method_ns_signal
66
+
67
+ # @!method broadcast
68
+ # @!macro synchronization_object_method_ns_broadcast
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,71 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ class MriLockableObject < AbstractLockableObject
7
+ protected
8
+
9
+ def ns_signal
10
+ @__condition__.signal
11
+ self
12
+ end
13
+
14
+ def ns_broadcast
15
+ @__condition__.broadcast
16
+ self
17
+ end
18
+ end
19
+
20
+
21
+ # @!visibility private
22
+ # @!macro internal_implementation_note
23
+ class MriMutexLockableObject < MriLockableObject
24
+ safe_initialization!
25
+
26
+ def initialize(*defaults)
27
+ super(*defaults)
28
+ @__lock__ = ::Mutex.new
29
+ @__condition__ = ::ConditionVariable.new
30
+ end
31
+
32
+ protected
33
+
34
+ def synchronize
35
+ if @__lock__.owned?
36
+ yield
37
+ else
38
+ @__lock__.synchronize { yield }
39
+ end
40
+ end
41
+
42
+ def ns_wait(timeout = nil)
43
+ @__condition__.wait @__lock__, timeout
44
+ self
45
+ end
46
+ end
47
+
48
+ # @!visibility private
49
+ # @!macro internal_implementation_note
50
+ class MriMonitorLockableObject < MriLockableObject
51
+ safe_initialization!
52
+
53
+ def initialize(*defaults)
54
+ super(*defaults)
55
+ @__lock__ = ::Monitor.new
56
+ @__condition__ = @__lock__.new_cond
57
+ end
58
+
59
+ protected
60
+
61
+ def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
62
+ @__lock__.synchronize { yield }
63
+ end
64
+
65
+ def ns_wait(timeout = nil)
66
+ @__condition__.wait timeout
67
+ self
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,43 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ module MriAttrVolatile
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def attr_volatile(*names)
11
+ names.each do |name|
12
+ ivar = :"@volatile_#{name}"
13
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
+ def #{name}
15
+ #{ivar}
16
+ end
17
+
18
+ def #{name}=(value)
19
+ #{ivar} = value
20
+ end
21
+ RUBY
22
+ end
23
+ names.map { |n| [n, :"#{n}="] }.flatten
24
+ end
25
+ end
26
+
27
+ def full_memory_barrier
28
+ # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
29
+ # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
30
+ end
31
+ end
32
+
33
+ # @!visibility private
34
+ # @!macro internal_implementation_note
35
+ class MriObject < AbstractObject
36
+ include MriAttrVolatile
37
+
38
+ def initialize
39
+ # nothing to do
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,83 +1,150 @@
1
- require 'concurrent/synchronization/java_object'
2
- require 'concurrent/synchronization/monitor_object'
3
- require 'concurrent/synchronization/mutex_object'
4
- require 'concurrent/synchronization/rbx_object'
5
-
6
1
  module Concurrent
7
2
  module Synchronization
8
3
 
9
4
  # @!visibility private
10
5
  # @!macro internal_implementation_note
11
- Implementation = case
12
- when Concurrent.on_jruby?
13
- JavaObject
14
- when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
15
- MonitorObject
16
- when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
17
- MutexObject
18
- when Concurrent.on_rbx?
19
- RbxObject
20
- else
21
- warn 'Possibly unsupported Ruby implementation'
22
- MutexObject
23
- end
24
- private_constant :Implementation
25
-
26
- # @!macro [attach] synchronization_object
27
- #
28
- # Safe synchronization under any Ruby implementation.
29
- # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
30
- # Provides a single layer which can improve its implementation over time without changes needed to
31
- # the classes using it. Use {Synchronization::Object} not this abstract class.
32
- #
33
- # @note this object does not support usage together with
34
- # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
35
- # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
36
- # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
37
- # `Thread#wakeup` will not work on all platforms.
38
- #
39
- # @see {Event} implementation as an example of this class use
40
- #
41
- # @example simple
42
- # class AnClass < Synchronization::Object
43
- # def initialize
44
- # super
45
- # synchronize { @value = 'asd' }
46
- # end
47
- #
48
- # def value
49
- # synchronize { @value }
50
- # end
51
- # end
52
- #
53
- class Object < Implementation
54
-
55
- # @!method initialize(*args, &block)
56
- # @!macro synchronization_object_method_initialize
57
-
58
- # @!method synchronize
59
- # @!macro synchronization_object_method_synchronize
60
-
61
- # @!method initialize(*args, &block)
62
- # @!macro synchronization_object_method_ns_initialize
63
-
64
- # @!method wait_until(timeout = nil, &condition)
65
- # @!macro synchronization_object_method_ns_wait_until
66
-
67
- # @!method wait(timeout = nil)
68
- # @!macro synchronization_object_method_ns_wait
69
-
70
- # @!method signal
71
- # @!macro synchronization_object_method_ns_signal
72
-
73
- # @!method broadcast
74
- # @!macro synchronization_object_method_ns_broadcast
75
-
76
- # @!method ensure_ivar_visibility!
77
- # @!macro synchronization_object_method_ensure_ivar_visibility
6
+ ObjectImplementation = case
7
+ when Concurrent.on_cruby?
8
+ MriObject
9
+ when Concurrent.on_jruby?
10
+ JRubyObject
11
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
12
+ RbxObject
13
+ else
14
+ MriObject
15
+ end
16
+ private_constant :ObjectImplementation
17
+
18
+ # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
19
+ # - final instance variables see {Object.safe_initialization!}
20
+ # - volatile instance variables see {Object.attr_volatile}
21
+ # - volatile instance variables see {Object.attr_atomic}
22
+ class Object < ObjectImplementation
78
23
 
79
24
  # @!method self.attr_volatile(*names)
80
- # @!macro synchronization_object_method_self_attr_volatile
25
+ # Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
26
+ # volatile (Java) semantic. The instance variable should be accessed oly through generated methods.
27
+ #
28
+ # @param [Array<Symbol>] names of the instance variables to be volatile
29
+ # @return [Array<Symbol>] names of defined method names
30
+
31
+ # Has to be called by children.
32
+ def initialize
33
+ super
34
+ initialize_volatile_with_cas
35
+ end
36
+
37
+ # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
38
+ # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
39
+ # same behaviour as Java's final fields.
40
+ # @example
41
+ # class AClass < Concurrent::Synchronization::Object
42
+ # safe_initialization!
43
+ #
44
+ # def initialize
45
+ # @AFinalValue = 'value' # published safely, does not have to be synchronized
46
+ # end
47
+ # end
48
+ def self.safe_initialization!
49
+ # define only once, and not again in children
50
+ return if safe_initialization?
51
+
52
+ def self.new(*)
53
+ object = super
54
+ ensure
55
+ object.full_memory_barrier if object
56
+ end
57
+
58
+ @safe_initialization = true
59
+ end
60
+
61
+ # @return [true, false] if this class is safely initialized.
62
+ def self.safe_initialization?
63
+ @safe_initialization = false unless defined? @safe_initialization
64
+ @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
65
+ end
66
+
67
+ # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
68
+ # any instance variables with CamelCase names and isn't {.safe_initialization?}.
69
+ def self.ensure_safe_initialization_when_final_fields_are_present
70
+ Object.class_eval do
71
+ def self.new(*)
72
+ object = super
73
+ ensure
74
+ has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
75
+ if has_final_field && !safe_initialization?
76
+ raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!"
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ # Creates methods for reading and writing to a instance variable with
83
+ # volatile (Java) semantic as {.attr_volatile} does.
84
+ # The instance variable should be accessed oly through generated methods.
85
+ # This method generates following methods: `value`, `value=(new_value) #=> new_value`,
86
+ # `swap_value(new_value) #=> old_value`,
87
+ # `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
88
+ # @param [Array<Symbol>] names of the instance variables to be volatile with CAS.
89
+ # @return [Array<Symbol>] names of defined method names.
90
+ def self.attr_atomic(*names)
91
+ @volatile_cas_fields ||= []
92
+ @volatile_cas_fields += names
93
+ safe_initialization!
94
+ define_initialize_volatile_with_cas
95
+
96
+ names.each do |name|
97
+ ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
98
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
99
+ def #{name}
100
+ #{ivar}.get
101
+ end
102
+
103
+ def #{name}=(value)
104
+ #{ivar}.set value
105
+ end
106
+
107
+ def swap_#{name}(value)
108
+ #{ivar}.swap value
109
+ end
110
+
111
+ def compare_and_set_#{name}(expected, value)
112
+ #{ivar}.compare_and_set expected, value
113
+ end
114
+
115
+ def update_#{name}(&block)
116
+ #{ivar}.update(&block)
117
+ end
118
+ RUBY
119
+ end
120
+ names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
121
+ end
122
+
123
+ # @param [true,false] inherited should inherited volatile with CAS fields be returned?
124
+ # @return [Array<Symbol>] Returns defined volatile with CAS fields on this class.
125
+ def self.volatile_cas_fields(inherited = true)
126
+ @volatile_cas_fields ||= []
127
+ ((superclass.volatile_cas_fields if superclass.respond_to?(:volatile_cas_fields) && inherited) || []) +
128
+ @volatile_cas_fields
129
+ end
130
+
131
+ private
132
+
133
+ def self.define_initialize_volatile_with_cas
134
+ assignments = @volatile_cas_fields.map { |name| "@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = AtomicReference.new(nil)" }.join("\n")
135
+ class_eval <<-RUBY
136
+ def initialize_volatile_with_cas
137
+ super
138
+ #{assignments}
139
+ end
140
+ RUBY
141
+ end
142
+
143
+ private_class_method :define_initialize_volatile_with_cas
144
+
145
+ def initialize_volatile_with_cas
146
+ end
147
+
81
148
  end
82
149
  end
83
150
  end