concurrent-ruby 1.0.5 → 1.1.10
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 +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
|