concurrent-ruby 1.0.5 → 1.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +155 -0
- data/Gemfile +37 -0
- data/LICENSE.txt +18 -18
- data/README.md +260 -103
- data/Rakefile +329 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +7 -7
- data/lib/concurrent-ruby/concurrent/array.rb +66 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +28 -24
- data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +7 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +34 -13
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +1 -2
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
- data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +35 -38
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +47 -33
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +20 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +29 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +7 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
- data/lib/concurrent-ruby/concurrent/map.rb +346 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +27 -16
- data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +54 -21
- data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
- data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
- data/lib/concurrent-ruby/concurrent/set.rb +74 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +12 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +15 -35
- data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -58
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -35
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
- metadata +149 -134
- data/lib/concurrent/array.rb +0 -39
- data/lib/concurrent/atomic/atomic_reference.rb +0 -51
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
- data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -16
- data/lib/concurrent/atomic_reference/rbx.rb +0 -22
- data/lib/concurrent/atomic_reference/ruby.rb +0 -32
- data/lib/concurrent/atomics.rb +0 -53
- data/lib/concurrent/edge.rb +0 -26
- data/lib/concurrent/hash.rb +0 -36
- data/lib/concurrent/lazy_register.rb +0 -81
- data/lib/concurrent/map.rb +0 -240
- data/lib/concurrent/synchronization/mri_lockable_object.rb +0 -71
- data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
- data/lib/concurrent/synchronization/truffle_object.rb +0 -31
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
- data/lib/concurrent/utility/at_exit.rb +0 -97
- data/lib/concurrent/utility/monotonic_time.rb +0 -58
- data/lib/concurrent/utility/native_extension_loader.rb +0 -73
- data/lib/concurrent/version.rb +0 -4
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# Methods form module A included to a module B, which is already included into class C,
|
4
|
+
# will not be visible in the C class. If this module is extended to B then A's methods
|
5
|
+
# are correctly made visible to C.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# module A
|
9
|
+
# def a
|
10
|
+
# :a
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# module B1
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class C1
|
18
|
+
# include B1
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# module B2
|
22
|
+
# extend Concurrent::ReInclude
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class C2
|
26
|
+
# include B2
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# B1.send :include, A
|
30
|
+
# B2.send :include, A
|
31
|
+
#
|
32
|
+
# C1.new.respond_to? :a # => false
|
33
|
+
# C2.new.respond_to? :a # => true
|
34
|
+
module ReInclude
|
35
|
+
# @!visibility private
|
36
|
+
def included(base)
|
37
|
+
(@re_include_to_bases ||= []) << [:include, base]
|
38
|
+
super(base)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def extended(base)
|
43
|
+
(@re_include_to_bases ||= []) << [:extend, base]
|
44
|
+
super(base)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
def include(*modules)
|
49
|
+
result = super(*modules)
|
50
|
+
modules.reverse.each do |module_being_included|
|
51
|
+
(@re_include_to_bases ||= []).each do |method, mod|
|
52
|
+
mod.send method, module_being_included
|
53
|
+
end
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -58,29 +58,42 @@ module Concurrent
|
|
58
58
|
# @example Basic usage
|
59
59
|
#
|
60
60
|
# require 'concurrent'
|
61
|
-
# require '
|
62
|
-
# require 'open-uri'
|
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
|
#
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/thread_safe/util'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
# @!macro concurrent_set
|
8
|
+
#
|
9
|
+
# A thread-safe subclass of Set. This version locks against the object
|
10
|
+
# itself for every method call, ensuring only one thread can be reading
|
11
|
+
# or writing at a time. This includes iteration methods like `#each`.
|
12
|
+
#
|
13
|
+
# @note `a += b` is **not** a **thread-safe** operation on
|
14
|
+
# `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
|
15
|
+
# which is union of `a` and `b`, then it writes the union to `a`.
|
16
|
+
# The read and write are independent operations they do not form a single atomic
|
17
|
+
# operation therefore when two `+=` operations are executed concurrently updates
|
18
|
+
# may be lost. Use `#merge` instead.
|
19
|
+
#
|
20
|
+
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
|
21
|
+
|
22
|
+
# @!macro internal_implementation_note
|
23
|
+
SetImplementation = case
|
24
|
+
when Concurrent.on_cruby?
|
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
|
35
|
+
|
36
|
+
when Concurrent.on_jruby?
|
37
|
+
require 'jruby/synchronized'
|
38
|
+
|
39
|
+
class JRubySet < ::Set
|
40
|
+
include JRuby::Synchronized
|
41
|
+
end
|
42
|
+
|
43
|
+
JRubySet
|
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
|
+
when Concurrent.on_truffleruby?
|
56
|
+
require 'concurrent/thread_safe/util/data_structures'
|
57
|
+
|
58
|
+
class TruffleRubySet < ::Set
|
59
|
+
end
|
60
|
+
|
61
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
|
62
|
+
TruffleRubySet
|
63
|
+
|
64
|
+
else
|
65
|
+
warn 'Possibly unsupported Ruby implementation'
|
66
|
+
::Set
|
67
|
+
end
|
68
|
+
private_constant :SetImplementation
|
69
|
+
|
70
|
+
# @!macro concurrent_set
|
71
|
+
class Set < SetImplementation
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -9,7 +9,7 @@ module Concurrent
|
|
9
9
|
# or any time thereafter. Attempting to assign a value to a member
|
10
10
|
# that has already been set will result in a `Concurrent::ImmutabilityError`.
|
11
11
|
#
|
12
|
-
# @see http://ruby-doc.org/core
|
12
|
+
# @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
|
13
13
|
# @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
|
14
14
|
module SettableStruct
|
15
15
|
include Synchronization::AbstractStruct
|
@@ -91,6 +91,16 @@ module Concurrent
|
|
91
91
|
raise NameError.new("no member '#{member}' in struct")
|
92
92
|
end
|
93
93
|
|
94
|
+
private
|
95
|
+
|
96
|
+
# @!visibility private
|
97
|
+
def initialize_copy(original)
|
98
|
+
synchronize do
|
99
|
+
super(original)
|
100
|
+
ns_initialize_copy
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
94
104
|
# @!macro struct_new
|
95
105
|
def self.new(*args, &block)
|
96
106
|
clazz_name = nil
|
@@ -107,6 +117,7 @@ module Concurrent
|
|
107
117
|
synchronize do
|
108
118
|
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block)
|
109
119
|
members.each_with_index do |member, index|
|
120
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
110
121
|
clazz.send(:define_method, member) do
|
111
122
|
synchronize { @values[index] }
|
112
123
|
end
|
data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb
RENAMED
@@ -6,7 +6,7 @@ module Concurrent
|
|
6
6
|
|
7
7
|
protected
|
8
8
|
|
9
|
-
# @!macro
|
9
|
+
# @!macro synchronization_object_method_synchronize
|
10
10
|
#
|
11
11
|
# @yield runs the block synchronized against this object,
|
12
12
|
# equivalent of java's `synchronize(this) {}`
|
@@ -15,7 +15,7 @@ module Concurrent
|
|
15
15
|
raise NotImplementedError
|
16
16
|
end
|
17
17
|
|
18
|
-
# @!macro
|
18
|
+
# @!macro synchronization_object_method_ns_wait_until
|
19
19
|
#
|
20
20
|
# Wait until condition is met or timeout passes,
|
21
21
|
# protects against spurious wake-ups.
|
@@ -45,7 +45,7 @@ module Concurrent
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# @!macro
|
48
|
+
# @!macro synchronization_object_method_ns_wait
|
49
49
|
#
|
50
50
|
# Wait until another thread calls #signal or #broadcast,
|
51
51
|
# spurious wake-ups can happen.
|
@@ -63,7 +63,7 @@ module Concurrent
|
|
63
63
|
raise NotImplementedError
|
64
64
|
end
|
65
65
|
|
66
|
-
# @!macro
|
66
|
+
# @!macro synchronization_object_method_ns_signal
|
67
67
|
#
|
68
68
|
# Signal one waiting thread.
|
69
69
|
# @return [self]
|
@@ -78,7 +78,7 @@ module Concurrent
|
|
78
78
|
raise NotImplementedError
|
79
79
|
end
|
80
80
|
|
81
|
-
# @!macro
|
81
|
+
# @!macro synchronization_object_method_ns_broadcast
|
82
82
|
#
|
83
83
|
# Broadcast to all waiting threads.
|
84
84
|
# @return [self]
|
@@ -11,7 +11,7 @@ module Concurrent
|
|
11
11
|
ns_initialize(*values)
|
12
12
|
end
|
13
13
|
|
14
|
-
# @!macro
|
14
|
+
# @!macro struct_length
|
15
15
|
#
|
16
16
|
# Returns the number of struct members.
|
17
17
|
#
|
@@ -21,7 +21,7 @@ module Concurrent
|
|
21
21
|
end
|
22
22
|
alias_method :size, :length
|
23
23
|
|
24
|
-
# @!macro
|
24
|
+
# @!macro struct_members
|
25
25
|
#
|
26
26
|
# Returns the struct members as an array of symbols.
|
27
27
|
#
|
@@ -115,9 +115,20 @@ module Concurrent
|
|
115
115
|
self.class.new(*self.to_h.merge(other, &block).values)
|
116
116
|
end
|
117
117
|
|
118
|
+
# @!visibility private
|
119
|
+
def ns_initialize_copy
|
120
|
+
@values = @values.map do |val|
|
121
|
+
begin
|
122
|
+
val.clone
|
123
|
+
rescue TypeError
|
124
|
+
val
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
118
129
|
# @!visibility private
|
119
130
|
def pr_underscore(clazz)
|
120
|
-
word = clazz.to_s
|
131
|
+
word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
|
121
132
|
word.gsub!(/::/, '/')
|
122
133
|
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
123
134
|
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
@@ -138,18 +149,21 @@ module Concurrent
|
|
138
149
|
end
|
139
150
|
unless name.nil?
|
140
151
|
begin
|
152
|
+
parent.send :remove_const, name if parent.const_defined?(name, false)
|
141
153
|
parent.const_set(name, clazz)
|
142
|
-
|
154
|
+
clazz
|
143
155
|
rescue NameError
|
144
156
|
raise NameError.new("identifier #{name} needs to be constant")
|
145
157
|
end
|
146
158
|
end
|
147
159
|
members.each_with_index do |member, index|
|
160
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
148
161
|
clazz.send(:define_method, member) do
|
149
162
|
@values[index]
|
150
163
|
end
|
151
164
|
end
|
152
165
|
clazz.class_exec(&block) unless block.nil?
|
166
|
+
clazz.singleton_class.send :alias_method, :[], :new
|
153
167
|
clazz
|
154
168
|
end
|
155
169
|
end
|
@@ -4,19 +4,17 @@ module Concurrent
|
|
4
4
|
# @!visibility private
|
5
5
|
# @!macro internal_implementation_note
|
6
6
|
LockableObjectImplementation = case
|
7
|
-
when Concurrent.on_cruby?
|
8
|
-
|
9
|
-
when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
|
10
|
-
MriMutexLockableObject
|
7
|
+
when Concurrent.on_cruby?
|
8
|
+
MutexLockableObject
|
11
9
|
when Concurrent.on_jruby?
|
12
10
|
JRubyLockableObject
|
13
11
|
when Concurrent.on_rbx?
|
14
12
|
RbxLockableObject
|
15
|
-
when Concurrent.
|
16
|
-
|
13
|
+
when Concurrent.on_truffleruby?
|
14
|
+
MutexLockableObject
|
17
15
|
else
|
18
16
|
warn 'Possibly unsupported Ruby implementation'
|
19
|
-
|
17
|
+
MonitorLockableObject
|
20
18
|
end
|
21
19
|
private_constant :LockableObjectImplementation
|
22
20
|
|
@@ -26,12 +24,12 @@ module Concurrent
|
|
26
24
|
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
27
25
|
#
|
28
26
|
# @note this object does not support usage together with
|
29
|
-
# [`Thread#wakeup`](http://ruby-doc.org/core
|
30
|
-
# and [`Thread#raise`](http://ruby-doc.org/core
|
27
|
+
# [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup)
|
28
|
+
# and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise).
|
31
29
|
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
32
30
|
# `Thread#wakeup` will not work on all platforms.
|
33
31
|
#
|
34
|
-
# @see
|
32
|
+
# @see Event implementation as an example of this class use
|
35
33
|
#
|
36
34
|
# @example simple
|
37
35
|
# class AnClass < Synchronization::Object
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Concurrent
|
2
|
+
# noinspection RubyInstanceVariableNamingConvention
|
3
|
+
module Synchronization
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
# @!macro internal_implementation_note
|
7
|
+
module ConditionSignalling
|
8
|
+
protected
|
9
|
+
|
10
|
+
def ns_signal
|
11
|
+
@__Condition__.signal
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def ns_broadcast
|
16
|
+
@__Condition__.broadcast
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
# @!macro internal_implementation_note
|
24
|
+
class MutexLockableObject < AbstractLockableObject
|
25
|
+
include ConditionSignalling
|
26
|
+
|
27
|
+
safe_initialization!
|
28
|
+
|
29
|
+
def initialize(*defaults)
|
30
|
+
super(*defaults)
|
31
|
+
@__Lock__ = ::Mutex.new
|
32
|
+
@__Condition__ = ::ConditionVariable.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_copy(other)
|
36
|
+
super
|
37
|
+
@__Lock__ = ::Mutex.new
|
38
|
+
@__Condition__ = ::ConditionVariable.new
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def synchronize
|
44
|
+
if @__Lock__.owned?
|
45
|
+
yield
|
46
|
+
else
|
47
|
+
@__Lock__.synchronize { yield }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ns_wait(timeout = nil)
|
52
|
+
@__Condition__.wait @__Lock__, timeout
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @!visibility private
|
58
|
+
# @!macro internal_implementation_note
|
59
|
+
class MonitorLockableObject < AbstractLockableObject
|
60
|
+
include ConditionSignalling
|
61
|
+
|
62
|
+
safe_initialization!
|
63
|
+
|
64
|
+
def initialize(*defaults)
|
65
|
+
super(*defaults)
|
66
|
+
@__Lock__ = ::Monitor.new
|
67
|
+
@__Condition__ = @__Lock__.new_cond
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize_copy(other)
|
71
|
+
super
|
72
|
+
@__Lock__ = ::Monitor.new
|
73
|
+
@__Condition__ = @__Lock__.new_cond
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
79
|
+
@__Lock__.synchronize { yield }
|
80
|
+
end
|
81
|
+
|
82
|
+
def ns_wait(timeout = nil)
|
83
|
+
@__Condition__.wait timeout
|
84
|
+
self
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -10,9 +10,10 @@ module Concurrent
|
|
10
10
|
JRubyObject
|
11
11
|
when Concurrent.on_rbx?
|
12
12
|
RbxObject
|
13
|
-
when Concurrent.
|
14
|
-
|
13
|
+
when Concurrent.on_truffleruby?
|
14
|
+
TruffleRubyObject
|
15
15
|
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
16
17
|
MriObject
|
17
18
|
end
|
18
19
|
private_constant :ObjectImplementation
|
@@ -26,15 +27,15 @@ module Concurrent
|
|
26
27
|
|
27
28
|
# @!method self.attr_volatile(*names)
|
28
29
|
# Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
|
29
|
-
# volatile (Java) semantic. The instance variable should be accessed
|
30
|
+
# volatile (Java) semantic. The instance variable should be accessed only through generated methods.
|
30
31
|
#
|
31
|
-
# @param [Array<Symbol>] names of the instance variables to be volatile
|
32
|
-
# @return [Array<Symbol>] names of defined method names
|
32
|
+
# @param [::Array<Symbol>] names of the instance variables to be volatile
|
33
|
+
# @return [::Array<Symbol>] names of defined method names
|
33
34
|
|
34
35
|
# Has to be called by children.
|
35
36
|
def initialize
|
36
37
|
super
|
37
|
-
|
38
|
+
__initialize_atomic_fields__
|
38
39
|
end
|
39
40
|
|
40
41
|
# By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
|
@@ -48,10 +49,12 @@ module Concurrent
|
|
48
49
|
# @AFinalValue = 'value' # published safely, does not have to be synchronized
|
49
50
|
# end
|
50
51
|
# end
|
52
|
+
# @return [true]
|
51
53
|
def self.safe_initialization!
|
52
54
|
# define only once, and not again in children
|
53
55
|
return if safe_initialization?
|
54
56
|
|
57
|
+
# @!visibility private
|
55
58
|
def self.new(*args, &block)
|
56
59
|
object = super(*args, &block)
|
57
60
|
ensure
|
@@ -69,6 +72,8 @@ module Concurrent
|
|
69
72
|
|
70
73
|
# For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
|
71
74
|
# any instance variables with CamelCase names and isn't {.safe_initialization?}.
|
75
|
+
# @raise when offend found
|
76
|
+
# @return [true]
|
72
77
|
def self.ensure_safe_initialization_when_final_fields_are_present
|
73
78
|
Object.class_eval do
|
74
79
|
def self.new(*args, &block)
|
@@ -80,6 +85,7 @@ module Concurrent
|
|
80
85
|
end
|
81
86
|
end
|
82
87
|
end
|
88
|
+
true
|
83
89
|
end
|
84
90
|
|
85
91
|
# Creates methods for reading and writing to a instance variable with
|
@@ -88,13 +94,30 @@ module Concurrent
|
|
88
94
|
# This method generates following methods: `value`, `value=(new_value) #=> new_value`,
|
89
95
|
# `swap_value(new_value) #=> old_value`,
|
90
96
|
# `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
|
91
|
-
# @param [Array<Symbol>] names of the instance variables to be volatile with CAS.
|
92
|
-
# @return [Array<Symbol>] names of defined method names.
|
97
|
+
# @param [::Array<Symbol>] names of the instance variables to be volatile with CAS.
|
98
|
+
# @return [::Array<Symbol>] names of defined method names.
|
99
|
+
# @!macro attr_atomic
|
100
|
+
# @!method $1
|
101
|
+
# @return [Object] The $1.
|
102
|
+
# @!method $1=(new_$1)
|
103
|
+
# Set the $1.
|
104
|
+
# @return [Object] new_$1.
|
105
|
+
# @!method swap_$1(new_$1)
|
106
|
+
# Set the $1 to new_$1 and return the old $1.
|
107
|
+
# @return [Object] old $1
|
108
|
+
# @!method compare_and_set_$1(expected_$1, new_$1)
|
109
|
+
# Sets the $1 to new_$1 if the current $1 is expected_$1
|
110
|
+
# @return [true, false]
|
111
|
+
# @!method update_$1(&block)
|
112
|
+
# Updates the $1 using the block.
|
113
|
+
# @yield [Object] Calculate a new $1 using given (old) $1
|
114
|
+
# @yieldparam [Object] old $1
|
115
|
+
# @return [Object] new $1
|
93
116
|
def self.attr_atomic(*names)
|
94
|
-
@
|
95
|
-
@
|
117
|
+
@__atomic_fields__ ||= []
|
118
|
+
@__atomic_fields__ += names
|
96
119
|
safe_initialization!
|
97
|
-
|
120
|
+
define_initialize_atomic_fields
|
98
121
|
|
99
122
|
names.each do |name|
|
100
123
|
ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
|
@@ -123,29 +146,36 @@ module Concurrent
|
|
123
146
|
names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
|
124
147
|
end
|
125
148
|
|
126
|
-
# @param [true,false] inherited should inherited volatile with CAS fields be returned?
|
127
|
-
# @return [Array<Symbol>] Returns defined volatile with CAS fields on this class.
|
128
|
-
def self.
|
129
|
-
@
|
130
|
-
((superclass.
|
131
|
-
|
149
|
+
# @param [true, false] inherited should inherited volatile with CAS fields be returned?
|
150
|
+
# @return [::Array<Symbol>] Returns defined volatile with CAS fields on this class.
|
151
|
+
def self.atomic_attributes(inherited = true)
|
152
|
+
@__atomic_fields__ ||= []
|
153
|
+
((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [true, false] is the attribute with name atomic?
|
157
|
+
def self.atomic_attribute?(name)
|
158
|
+
atomic_attributes.include? name
|
132
159
|
end
|
133
160
|
|
134
161
|
private
|
135
162
|
|
136
|
-
def self.
|
137
|
-
assignments = @
|
138
|
-
|
139
|
-
|
163
|
+
def self.define_initialize_atomic_fields
|
164
|
+
assignments = @__atomic_fields__.map do |name|
|
165
|
+
"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)"
|
166
|
+
end.join("\n")
|
167
|
+
|
168
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
169
|
+
def __initialize_atomic_fields__
|
140
170
|
super
|
141
171
|
#{assignments}
|
142
172
|
end
|
143
173
|
RUBY
|
144
174
|
end
|
145
175
|
|
146
|
-
private_class_method :
|
176
|
+
private_class_method :define_initialize_atomic_fields
|
147
177
|
|
148
|
-
def
|
178
|
+
def __initialize_atomic_fields__
|
149
179
|
end
|
150
180
|
|
151
181
|
end
|