This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
require 'concurrent/utility/native_extension_loader' # load native parts first
|
||||
require 'concurrent/utility/monotonic_time'
|
||||
require 'concurrent/synchronization/object'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
class AbstractLockableObject < Synchronization::Object
|
||||
|
||||
protected
|
||||
|
||||
# @!macro synchronization_object_method_synchronize
|
||||
#
|
||||
# @yield runs the block synchronized against this object,
|
||||
# equivalent of java's `synchronize(this) {}`
|
||||
# @note can by made public in descendants if required by `public :synchronize`
|
||||
def synchronize
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# @!macro synchronization_object_method_ns_wait_until
|
||||
#
|
||||
# Wait until condition is met or timeout passes,
|
||||
# protects against spurious wake-ups.
|
||||
# @param [Numeric, nil] timeout in seconds, `nil` means no timeout
|
||||
# @yield condition to be met
|
||||
# @yieldreturn [true, false]
|
||||
# @return [true, false] if condition met
|
||||
# @note only to be used inside synchronized block
|
||||
# @note to provide direct access to this method in a descendant add method
|
||||
# ```
|
||||
# def wait_until(timeout = nil, &condition)
|
||||
# synchronize { ns_wait_until(timeout, &condition) }
|
||||
# end
|
||||
# ```
|
||||
def ns_wait_until(timeout = nil, &condition)
|
||||
if timeout
|
||||
wait_until = Concurrent.monotonic_time + timeout
|
||||
loop do
|
||||
now = Concurrent.monotonic_time
|
||||
condition_result = condition.call
|
||||
return condition_result if now >= wait_until || condition_result
|
||||
ns_wait wait_until - now
|
||||
end
|
||||
else
|
||||
ns_wait timeout until condition.call
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# @!macro synchronization_object_method_ns_wait
|
||||
#
|
||||
# Wait until another thread calls #signal or #broadcast,
|
||||
# spurious wake-ups can happen.
|
||||
#
|
||||
# @param [Numeric, nil] timeout in seconds, `nil` means no timeout
|
||||
# @return [self]
|
||||
# @note only to be used inside synchronized block
|
||||
# @note to provide direct access to this method in a descendant add method
|
||||
# ```
|
||||
# def wait(timeout = nil)
|
||||
# synchronize { ns_wait(timeout) }
|
||||
# end
|
||||
# ```
|
||||
def ns_wait(timeout = nil)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# @!macro synchronization_object_method_ns_signal
|
||||
#
|
||||
# Signal one waiting thread.
|
||||
# @return [self]
|
||||
# @note only to be used inside synchronized block
|
||||
# @note to provide direct access to this method in a descendant add method
|
||||
# ```
|
||||
# def signal
|
||||
# synchronize { ns_signal }
|
||||
# end
|
||||
# ```
|
||||
def ns_signal
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# @!macro synchronization_object_method_ns_broadcast
|
||||
#
|
||||
# Broadcast to all waiting threads.
|
||||
# @return [self]
|
||||
# @note only to be used inside synchronized block
|
||||
# @note to provide direct access to this method in a descendant add method
|
||||
# ```
|
||||
# def broadcast
|
||||
# synchronize { ns_broadcast }
|
||||
# end
|
||||
# ```
|
||||
def ns_broadcast
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
class AbstractObject
|
||||
def initialize
|
||||
# nothing to do
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
# @abstract
|
||||
def full_memory_barrier
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.attr_volatile(*names)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
module AbstractStruct
|
||||
|
||||
# @!visibility private
|
||||
def initialize(*values)
|
||||
super()
|
||||
ns_initialize(*values)
|
||||
end
|
||||
|
||||
# @!macro struct_length
|
||||
#
|
||||
# Returns the number of struct members.
|
||||
#
|
||||
# @return [Fixnum] the number of struct members
|
||||
def length
|
||||
self.class::MEMBERS.length
|
||||
end
|
||||
alias_method :size, :length
|
||||
|
||||
# @!macro struct_members
|
||||
#
|
||||
# Returns the struct members as an array of symbols.
|
||||
#
|
||||
# @return [Array] the struct members as an array of symbols
|
||||
def members
|
||||
self.class::MEMBERS.dup
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# @!macro struct_values
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_values
|
||||
@values.dup
|
||||
end
|
||||
|
||||
# @!macro struct_values_at
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_values_at(indexes)
|
||||
@values.values_at(*indexes)
|
||||
end
|
||||
|
||||
# @!macro struct_to_h
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_to_h
|
||||
length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
|
||||
end
|
||||
|
||||
# @!macro struct_get
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_get(member)
|
||||
if member.is_a? Integer
|
||||
if member >= @values.length
|
||||
raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
|
||||
end
|
||||
@values[member]
|
||||
else
|
||||
send(member)
|
||||
end
|
||||
rescue NoMethodError
|
||||
raise NameError.new("no member '#{member}' in struct")
|
||||
end
|
||||
|
||||
# @!macro struct_equality
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_equality(other)
|
||||
self.class == other.class && self.values == other.values
|
||||
end
|
||||
|
||||
# @!macro struct_each
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_each
|
||||
values.each{|value| yield value }
|
||||
end
|
||||
|
||||
# @!macro struct_each_pair
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_each_pair
|
||||
@values.length.times do |index|
|
||||
yield self.class::MEMBERS[index], @values[index]
|
||||
end
|
||||
end
|
||||
|
||||
# @!macro struct_select
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_select
|
||||
values.select{|value| yield value }
|
||||
end
|
||||
|
||||
# @!macro struct_inspect
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_inspect
|
||||
struct = pr_underscore(self.class.ancestors[1])
|
||||
clazz = ((self.class.to_s =~ /^#<Class:/) == 0) ? '' : " #{self.class}"
|
||||
"#<#{struct}#{clazz} #{ns_to_h}>"
|
||||
end
|
||||
|
||||
# @!macro struct_merge
|
||||
#
|
||||
# @!visibility private
|
||||
def ns_merge(other, &block)
|
||||
self.class.new(*self.to_h.merge(other, &block).values)
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
def ns_initialize_copy
|
||||
@values = @values.map do |val|
|
||||
begin
|
||||
val.clone
|
||||
rescue TypeError
|
||||
val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
def pr_underscore(clazz)
|
||||
word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
|
||||
word.gsub!(/::/, '/')
|
||||
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
||||
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
||||
word.tr!("-", "_")
|
||||
word.downcase!
|
||||
word
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
def self.define_struct_class(parent, base, name, members, &block)
|
||||
clazz = Class.new(base || Object) do
|
||||
include parent
|
||||
self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
|
||||
def ns_initialize(*values)
|
||||
raise ArgumentError.new('struct size differs') if values.length > length
|
||||
@values = values.fill(nil, values.length..length-1)
|
||||
end
|
||||
end
|
||||
unless name.nil?
|
||||
begin
|
||||
parent.send :remove_const, name if parent.const_defined?(name, false)
|
||||
parent.const_set(name, clazz)
|
||||
clazz
|
||||
rescue NameError
|
||||
raise NameError.new("identifier #{name} needs to be constant")
|
||||
end
|
||||
end
|
||||
members.each_with_index do |member, index|
|
||||
clazz.send :remove_method, member if clazz.instance_methods(false).include? member
|
||||
clazz.send(:define_method, member) do
|
||||
@values[index]
|
||||
end
|
||||
end
|
||||
clazz.class_exec(&block) unless block.nil?
|
||||
clazz.singleton_class.send :alias_method, :[], :new
|
||||
clazz
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
require 'concurrent/synchronization/lockable_object'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# TODO (pitr-ch 04-Dec-2016): should be in edge
|
||||
class Condition < LockableObject
|
||||
safe_initialization!
|
||||
|
||||
# TODO (pitr 12-Sep-2015): locks two objects, improve
|
||||
# TODO (pitr 26-Sep-2015): study
|
||||
# http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/concurrent/locks/AbstractQueuedSynchronizer.java#AbstractQueuedSynchronizer.Node
|
||||
|
||||
singleton_class.send :alias_method, :private_new, :new
|
||||
private_class_method :new
|
||||
|
||||
def initialize(lock)
|
||||
super()
|
||||
@Lock = lock
|
||||
end
|
||||
|
||||
def wait(timeout = nil)
|
||||
@Lock.synchronize { ns_wait(timeout) }
|
||||
end
|
||||
|
||||
def ns_wait(timeout = nil)
|
||||
synchronize { super(timeout) }
|
||||
end
|
||||
|
||||
def wait_until(timeout = nil, &condition)
|
||||
@Lock.synchronize { ns_wait_until(timeout, &condition) }
|
||||
end
|
||||
|
||||
def ns_wait_until(timeout = nil, &condition)
|
||||
synchronize { super(timeout, &condition) }
|
||||
end
|
||||
|
||||
def signal
|
||||
@Lock.synchronize { ns_signal }
|
||||
end
|
||||
|
||||
def ns_signal
|
||||
synchronize { super }
|
||||
end
|
||||
|
||||
def broadcast
|
||||
@Lock.synchronize { ns_broadcast }
|
||||
end
|
||||
|
||||
def ns_broadcast
|
||||
synchronize { super }
|
||||
end
|
||||
end
|
||||
|
||||
class LockableObject < LockableObjectImplementation
|
||||
def new_condition
|
||||
Condition.private_new(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
require 'concurrent/utility/native_extension_loader' # load native parts first
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
case
|
||||
when Concurrent.on_cruby?
|
||||
def self.full_memory_barrier
|
||||
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
||||
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
||||
end
|
||||
|
||||
when Concurrent.on_jruby?
|
||||
require 'concurrent/utility/native_extension_loader'
|
||||
def self.full_memory_barrier
|
||||
JRubyAttrVolatile.full_memory_barrier
|
||||
end
|
||||
|
||||
when Concurrent.on_truffleruby?
|
||||
def self.full_memory_barrier
|
||||
TruffleRuby.full_memory_barrier
|
||||
end
|
||||
|
||||
else
|
||||
warn 'Possibly unsupported Ruby implementation'
|
||||
def self.full_memory_barrier
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
require 'concurrent/utility/native_extension_loader' # load native parts first
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
if Concurrent.on_jruby?
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
class JRubyLockableObject < AbstractLockableObject
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
require 'concurrent/synchronization/lockable_object'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# TODO (pitr-ch 04-Dec-2016): should be in edge
|
||||
class Lock < LockableObject
|
||||
# TODO use JavaReentrantLock on JRuby
|
||||
|
||||
public :synchronize
|
||||
|
||||
def wait(timeout = nil)
|
||||
synchronize { ns_wait(timeout) }
|
||||
end
|
||||
|
||||
public :ns_wait
|
||||
|
||||
def wait_until(timeout = nil, &condition)
|
||||
synchronize { ns_wait_until(timeout, &condition) }
|
||||
end
|
||||
|
||||
public :ns_wait_until
|
||||
|
||||
def signal
|
||||
synchronize { ns_signal }
|
||||
end
|
||||
|
||||
public :ns_signal
|
||||
|
||||
def broadcast
|
||||
synchronize { ns_broadcast }
|
||||
end
|
||||
|
||||
public :ns_broadcast
|
||||
end
|
||||
end
|
||||
end
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
require 'concurrent/utility/engine'
|
||||
require 'concurrent/synchronization/abstract_lockable_object'
|
||||
require 'concurrent/synchronization/mutex_lockable_object'
|
||||
require 'concurrent/synchronization/jruby_lockable_object'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
LockableObjectImplementation = case
|
||||
when Concurrent.on_cruby?
|
||||
MutexLockableObject
|
||||
when Concurrent.on_jruby?
|
||||
JRubyLockableObject
|
||||
when Concurrent.on_truffleruby?
|
||||
MutexLockableObject
|
||||
else
|
||||
warn 'Possibly unsupported Ruby implementation'
|
||||
MonitorLockableObject
|
||||
end
|
||||
private_constant :LockableObjectImplementation
|
||||
|
||||
# Safe synchronization under any Ruby implementation.
|
||||
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
||||
# Provides a single layer which can improve its implementation over time without changes needed to
|
||||
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
||||
#
|
||||
# @note this object does not support usage together with
|
||||
# [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup)
|
||||
# and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise).
|
||||
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
||||
# `Thread#wakeup` will not work on all platforms.
|
||||
#
|
||||
# @see Event implementation as an example of this class use
|
||||
#
|
||||
# @example simple
|
||||
# class AnClass < Synchronization::Object
|
||||
# def initialize
|
||||
# super
|
||||
# synchronize { @value = 'asd' }
|
||||
# end
|
||||
#
|
||||
# def value
|
||||
# synchronize { @value }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @!visibility private
|
||||
class LockableObject < LockableObjectImplementation
|
||||
|
||||
# TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
|
||||
# TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
|
||||
|
||||
# @!method initialize(*args, &block)
|
||||
# @!macro synchronization_object_method_initialize
|
||||
|
||||
# @!method synchronize
|
||||
# @!macro synchronization_object_method_synchronize
|
||||
|
||||
# @!method wait_until(timeout = nil, &condition)
|
||||
# @!macro synchronization_object_method_ns_wait_until
|
||||
|
||||
# @!method wait(timeout = nil)
|
||||
# @!macro synchronization_object_method_ns_wait
|
||||
|
||||
# @!method signal
|
||||
# @!macro synchronization_object_method_ns_signal
|
||||
|
||||
# @!method broadcast
|
||||
# @!macro synchronization_object_method_ns_broadcast
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
require 'concurrent/synchronization/abstract_lockable_object'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
module ConditionSignalling
|
||||
protected
|
||||
|
||||
def ns_signal
|
||||
@__Condition__.signal
|
||||
self
|
||||
end
|
||||
|
||||
def ns_broadcast
|
||||
@__Condition__.broadcast
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
class MutexLockableObject < AbstractLockableObject
|
||||
include ConditionSignalling
|
||||
|
||||
safe_initialization!
|
||||
|
||||
def initialize
|
||||
super()
|
||||
@__Lock__ = ::Mutex.new
|
||||
@__Condition__ = ::ConditionVariable.new
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
super
|
||||
@__Lock__ = ::Mutex.new
|
||||
@__Condition__ = ::ConditionVariable.new
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def synchronize
|
||||
if @__Lock__.owned?
|
||||
yield
|
||||
else
|
||||
@__Lock__.synchronize { yield }
|
||||
end
|
||||
end
|
||||
|
||||
def ns_wait(timeout = nil)
|
||||
@__Condition__.wait @__Lock__, timeout
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
class MonitorLockableObject < AbstractLockableObject
|
||||
include ConditionSignalling
|
||||
|
||||
safe_initialization!
|
||||
|
||||
def initialize
|
||||
super()
|
||||
@__Lock__ = ::Monitor.new
|
||||
@__Condition__ = @__Lock__.new_cond
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
super
|
||||
@__Lock__ = ::Monitor.new
|
||||
@__Condition__ = @__Lock__.new_cond
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
||||
@__Lock__.synchronize { yield }
|
||||
end
|
||||
|
||||
def ns_wait(timeout = nil)
|
||||
@__Condition__.wait timeout
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+151
@@ -0,0 +1,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
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
require 'concurrent/synchronization/full_memory_barrier'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
#
|
||||
# By extending this module, a class and all its children are marked to be constructed safely. Meaning that
|
||||
# all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
|
||||
# same behaviour as Java's final fields.
|
||||
#
|
||||
# Due to using Kernel#extend, the module is not included again if already present in the ancestors,
|
||||
# which avoids extra overhead.
|
||||
#
|
||||
# @example
|
||||
# class AClass < Concurrent::Synchronization::Object
|
||||
# extend Concurrent::Synchronization::SafeInitialization
|
||||
#
|
||||
# def initialize
|
||||
# @AFinalValue = 'value' # published safely, #foo will never return nil
|
||||
# end
|
||||
#
|
||||
# def foo
|
||||
# @AFinalValue
|
||||
# end
|
||||
# end
|
||||
module SafeInitialization
|
||||
def new(*args, &block)
|
||||
super(*args, &block)
|
||||
ensure
|
||||
Concurrent::Synchronization.full_memory_barrier
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
require 'concurrent/utility/native_extension_loader' # load native parts first
|
||||
require 'concurrent/utility/engine'
|
||||
require 'concurrent/synchronization/full_memory_barrier'
|
||||
|
||||
module Concurrent
|
||||
module Synchronization
|
||||
|
||||
# Volatile adds the attr_volatile class method when included.
|
||||
#
|
||||
# @example
|
||||
# class Foo
|
||||
# include Concurrent::Synchronization::Volatile
|
||||
#
|
||||
# attr_volatile :bar
|
||||
#
|
||||
# def initialize
|
||||
# self.bar = 1
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# foo = Foo.new
|
||||
# foo.bar
|
||||
# => 1
|
||||
# foo.bar = 2
|
||||
# => 2
|
||||
#
|
||||
# @!visibility private
|
||||
module Volatile
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def full_memory_barrier
|
||||
Synchronization.full_memory_barrier
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
if Concurrent.on_cruby?
|
||||
def attr_volatile(*names)
|
||||
names.each do |name|
|
||||
ivar = :"@volatile_#{name}"
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{name}
|
||||
#{ivar}
|
||||
end
|
||||
|
||||
def #{name}=(value)
|
||||
#{ivar} = value
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
names.map { |n| [n, :"#{n}="] }.flatten
|
||||
end
|
||||
|
||||
elsif Concurrent.on_jruby?
|
||||
def attr_volatile(*names)
|
||||
names.each do |name|
|
||||
ivar = :"@volatile_#{name}"
|
||||
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{name}
|
||||
::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_get_volatile(self, :#{ivar})
|
||||
end
|
||||
|
||||
def #{name}=(value)
|
||||
::Concurrent::Synchronization::JRubyAttrVolatile.instance_variable_set_volatile(self, :#{ivar}, value)
|
||||
end
|
||||
RUBY
|
||||
|
||||
end
|
||||
names.map { |n| [n, :"#{n}="] }.flatten
|
||||
end
|
||||
|
||||
else
|
||||
warn 'Possibly unsupported Ruby implementation' unless Concurrent.on_truffleruby?
|
||||
|
||||
def attr_volatile(*names)
|
||||
names.each do |name|
|
||||
ivar = :"@volatile_#{name}"
|
||||
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{name}
|
||||
::Concurrent::Synchronization.full_memory_barrier
|
||||
#{ivar}
|
||||
end
|
||||
|
||||
def #{name}=(value)
|
||||
#{ivar} = value
|
||||
::Concurrent::Synchronization.full_memory_barrier
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
names.map { |n| [n, :"#{n}="] }.flatten
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user