concurrent-ruby 1.1.8 → 1.2.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -0
- data/Gemfile +2 -8
- data/README.md +49 -28
- data/Rakefile +66 -81
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
- data/lib/concurrent-ruby/concurrent/array.rb +0 -10
- data/lib/concurrent-ruby/concurrent/async.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
- data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
- data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/map.rb +43 -30
- data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
- data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
- data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
- data/lib/concurrent-ruby/concurrent/set.rb +12 -14
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
- data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
- data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
- metadata +11 -12
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -65
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -57,30 +57,43 @@ module Concurrent
|
|
57
57
|
#
|
58
58
|
# @example Basic usage
|
59
59
|
#
|
60
|
-
# require 'concurrent'
|
61
|
-
# require '
|
62
|
-
# require '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
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
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
|
-
#
|
76
|
-
# price.value #=>
|
77
|
-
# price.state #=> :fulfilled
|
88
|
+
# price.pending? #=> true
|
89
|
+
# price.value(0) #=> nil (does not block)
|
78
90
|
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
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
|
#
|
@@ -19,13 +19,19 @@ module Concurrent
|
|
19
19
|
#
|
20
20
|
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
|
21
21
|
|
22
|
-
|
23
22
|
# @!macro internal_implementation_note
|
24
23
|
SetImplementation = case
|
25
24
|
when Concurrent.on_cruby?
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
25
|
+
# The CRuby implementation of Set is written in Ruby itself and is
|
26
|
+
# not thread safe for certain methods.
|
27
|
+
require 'monitor'
|
28
|
+
require 'concurrent/thread_safe/util/data_structures'
|
29
|
+
|
30
|
+
class CRubySet < ::Set
|
31
|
+
end
|
32
|
+
|
33
|
+
ThreadSafe::Util.make_synchronized_on_cruby CRubySet
|
34
|
+
CRubySet
|
29
35
|
|
30
36
|
when Concurrent.on_jruby?
|
31
37
|
require 'jruby/synchronized'
|
@@ -33,16 +39,8 @@ module Concurrent
|
|
33
39
|
class JRubySet < ::Set
|
34
40
|
include JRuby::Synchronized
|
35
41
|
end
|
36
|
-
JRubySet
|
37
|
-
|
38
|
-
when Concurrent.on_rbx?
|
39
|
-
require 'monitor'
|
40
|
-
require 'concurrent/thread_safe/util/data_structures'
|
41
42
|
|
42
|
-
|
43
|
-
end
|
44
|
-
ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
|
45
|
-
RbxSet
|
43
|
+
JRubySet
|
46
44
|
|
47
45
|
when Concurrent.on_truffleruby?
|
48
46
|
require 'concurrent/thread_safe/util/data_structures'
|
@@ -50,7 +48,7 @@ module Concurrent
|
|
50
48
|
class TruffleRubySet < ::Set
|
51
49
|
end
|
52
50
|
|
53
|
-
ThreadSafe::Util.make_synchronized_on_truffleruby
|
51
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
|
54
52
|
TruffleRubySet
|
55
53
|
|
56
54
|
else
|
@@ -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
|
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
|
-
|
8
|
+
# nothing to do
|
11
9
|
end
|
12
10
|
|
13
11
|
# @!visibility private
|
@@ -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?
|
6
|
+
if Concurrent.on_jruby?
|
5
7
|
|
6
8
|
# @!visibility private
|
7
9
|
# @!macro internal_implementation_note
|
@@ -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?
|
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,14 @@ module Concurrent
|
|
26
27
|
|
27
28
|
safe_initialization!
|
28
29
|
|
29
|
-
def initialize
|
30
|
-
super(
|
30
|
+
def initialize
|
31
|
+
super()
|
32
|
+
@__Lock__ = ::Mutex.new
|
33
|
+
@__Condition__ = ::ConditionVariable.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize_copy(other)
|
37
|
+
super
|
31
38
|
@__Lock__ = ::Mutex.new
|
32
39
|
@__Condition__ = ::ConditionVariable.new
|
33
40
|
end
|
@@ -55,8 +62,14 @@ module Concurrent
|
|
55
62
|
|
56
63
|
safe_initialization!
|
57
64
|
|
58
|
-
def initialize
|
59
|
-
super(
|
65
|
+
def initialize
|
66
|
+
super()
|
67
|
+
@__Lock__ = ::Monitor.new
|
68
|
+
@__Condition__ = @__Lock__.new_cond
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize_copy(other)
|
72
|
+
super
|
60
73
|
@__Lock__ = ::Monitor.new
|
61
74
|
@__Condition__ = @__Lock__.new_cond
|
62
75
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
26
|
-
# {include:file:docs-source/synchronization-notes.md}
|
9
|
+
# @!visibility private
|
27
10
|
module Synchronization
|
28
11
|
end
|
29
12
|
end
|
@@ -2,49 +2,46 @@ require 'delegate'
|
|
2
2
|
require 'monitor'
|
3
3
|
|
4
4
|
module Concurrent
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
Thread.abort_on_exception = true
|
27
|
-
end
|
5
|
+
# This class provides a trivial way to synchronize all calls to a given object
|
6
|
+
# by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
|
7
|
+
# around the delegated `#send`. Example:
|
8
|
+
#
|
9
|
+
# array = [] # not thread-safe on many impls
|
10
|
+
# array = SynchronizedDelegator.new([]) # thread-safe
|
11
|
+
#
|
12
|
+
# A simple `Monitor` provides a very coarse-grained way to synchronize a given
|
13
|
+
# object, in that it will cause synchronization for methods that have no need
|
14
|
+
# for it, but this is a trivial way to get thread-safety where none may exist
|
15
|
+
# currently on some implementations.
|
16
|
+
#
|
17
|
+
# This class is currently being considered for inclusion into stdlib, via
|
18
|
+
# https://bugs.ruby-lang.org/issues/8556
|
19
|
+
#
|
20
|
+
# @!visibility private
|
21
|
+
class SynchronizedDelegator < SimpleDelegator
|
22
|
+
def setup
|
23
|
+
@old_abort = Thread.abort_on_exception
|
24
|
+
Thread.abort_on_exception = true
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
def teardown
|
28
|
+
Thread.abort_on_exception = @old_abort
|
29
|
+
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
def initialize(obj)
|
32
|
+
__setobj__(obj)
|
33
|
+
@monitor = Monitor.new
|
34
|
+
end
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
36
|
+
def method_missing(method, *args, &block)
|
37
|
+
monitor = @monitor
|
38
|
+
begin
|
39
|
+
monitor.enter
|
40
|
+
super
|
41
|
+
ensure
|
42
|
+
monitor.exit
|
46
43
|
end
|
47
|
-
|
48
44
|
end
|
45
|
+
|
49
46
|
end
|
50
47
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'concurrent/thread_safe/util'
|
2
2
|
require 'concurrent/thread_safe/util/volatile'
|
3
|
+
require 'concurrent/utility/engine'
|
3
4
|
|
4
5
|
module Concurrent
|
5
6
|
|
@@ -32,45 +33,7 @@ module Concurrent
|
|
32
33
|
# @!visibility private
|
33
34
|
module CheapLockable
|
34
35
|
private
|
35
|
-
|
36
|
-
if engine == 'rbx'
|
37
|
-
# Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects.
|
38
|
-
def cheap_synchronize
|
39
|
-
Rubinius.lock(self)
|
40
|
-
begin
|
41
|
-
yield
|
42
|
-
ensure
|
43
|
-
Rubinius.unlock(self)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def cheap_wait
|
48
|
-
wchan = Rubinius::Channel.new
|
49
|
-
|
50
|
-
begin
|
51
|
-
waiters = @waiters ||= []
|
52
|
-
waiters.push wchan
|
53
|
-
Rubinius.unlock(self)
|
54
|
-
signaled = wchan.receive_timeout nil
|
55
|
-
ensure
|
56
|
-
Rubinius.lock(self)
|
57
|
-
|
58
|
-
unless signaled or waiters.delete(wchan)
|
59
|
-
# we timed out, but got signaled afterwards (e.g. while waiting to
|
60
|
-
# acquire @lock), so pass that signal on to the next waiter
|
61
|
-
waiters.shift << true unless waiters.empty?
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
self
|
66
|
-
end
|
67
|
-
|
68
|
-
def cheap_broadcast
|
69
|
-
waiters = @waiters ||= []
|
70
|
-
waiters.shift << true until waiters.empty?
|
71
|
-
self
|
72
|
-
end
|
73
|
-
elsif engine == 'jruby'
|
36
|
+
if Concurrent.on_jruby?
|
74
37
|
# Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects
|
75
38
|
require 'jruby'
|
76
39
|
|