1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
require 'concurrent/utility/native_extension_loader' # load native parts first
require 'concurrent/synchronization/safe_initialization'
require 'concurrent/synchronization/volatile'
require 'concurrent/atomic/atomic_reference'
module Concurrent
module Synchronization
# Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
# - final instance variables see {Object.safe_initialization!}
# - volatile instance variables see {Object.attr_volatile}
# - volatile instance variables see {Object.attr_atomic}
# @!visibility private
class Object < AbstractObject
include Volatile
# TODO make it a module if possible
# @!method self.attr_volatile(*names)
# Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
# volatile (Java) semantic. The instance variable should be accessed only through generated methods.
#
# @param [::Array<Symbol>] names of the instance variables to be volatile
# @return [::Array<Symbol>] names of defined method names
# Has to be called by children.
def initialize
super
__initialize_atomic_fields__
end
def self.safe_initialization!
extend SafeInitialization unless safe_initialization?
end
def self.safe_initialization?
self.singleton_class < SafeInitialization
end
# For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
# any instance variables with CamelCase names and isn't {.safe_initialization?}.
# @raise when offend found
# @return [true]
def self.ensure_safe_initialization_when_final_fields_are_present
Object.class_eval do
def self.new(*args, &block)
object = super(*args, &block)
ensure
has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
if has_final_field && !safe_initialization?
raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!"
end
end
end
true
end
# Creates methods for reading and writing to a instance variable with
# volatile (Java) semantic as {.attr_volatile} does.
# The instance variable should be accessed only through generated methods.
# This method generates following methods: `value`, `value=(new_value) #=> new_value`,
# `swap_value(new_value) #=> old_value`,
# `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
# @param [::Array<Symbol>] names of the instance variables to be volatile with CAS.
# @return [::Array<Symbol>] names of defined method names.
# @!macro attr_atomic
# @!method $1
# @return [Object] The $1.
# @!method $1=(new_$1)
# Set the $1.
# @return [Object] new_$1.
# @!method swap_$1(new_$1)
# Set the $1 to new_$1 and return the old $1.
# @return [Object] old $1
# @!method compare_and_set_$1(expected_$1, new_$1)
# Sets the $1 to new_$1 if the current $1 is expected_$1
# @return [true, false]
# @!method update_$1(&block)
# Updates the $1 using the block.
# @yield [Object] Calculate a new $1 using given (old) $1
# @yieldparam [Object] old $1
# @return [Object] new $1
def self.attr_atomic(*names)
@__atomic_fields__ ||= []
@__atomic_fields__ += names
safe_initialization!
define_initialize_atomic_fields
names.each do |name|
ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
#{ivar}.get
end
def #{name}=(value)
#{ivar}.set value
end
def swap_#{name}(value)
#{ivar}.swap value
end
def compare_and_set_#{name}(expected, value)
#{ivar}.compare_and_set expected, value
end
def update_#{name}(&block)
#{ivar}.update(&block)
end
RUBY
end
names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
end
# @param [true, false] inherited should inherited volatile with CAS fields be returned?
# @return [::Array<Symbol>] Returns defined volatile with CAS fields on this class.
def self.atomic_attributes(inherited = true)
@__atomic_fields__ ||= []
((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__
end
# @return [true, false] is the attribute with name atomic?
def self.atomic_attribute?(name)
atomic_attributes.include? name
end
private
def self.define_initialize_atomic_fields
assignments = @__atomic_fields__.map do |name|
"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)"
end.join("\n")
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def __initialize_atomic_fields__
super
#{assignments}
end
RUBY
end
private_class_method :define_initialize_atomic_fields
def __initialize_atomic_fields__
end
end
end
end
|