concurrent-ruby 1.1.10 → 1.3.3
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 +47 -1
- data/Gemfile +1 -2
- data/README.md +23 -20
- data/Rakefile +75 -65
- 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 +3 -13
- 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 +81 -151
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
- 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 +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +5 -3
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
- 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/synchronized_map_backend.rb +23 -20
- 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 +1 -1
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +4 -0
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +6 -9
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +5 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
- 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/executor/timer_set.rb +6 -2
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
- 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 -39
- 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 +1 -1
- data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +1 -1
- data/lib/concurrent-ruby/concurrent/set.rb +0 -10
- 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 +5 -2
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -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/data_structures.rb +1 -37
- data/lib/concurrent-ruby/concurrent/timer_task.rb +59 -9
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
- 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 +118 -58
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +13 -17
- 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/collection/map/atomic_reference_map_backend.rb +0 -927
- 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 -71
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
@@ -3,6 +3,7 @@ require 'rbconfig'
|
|
3
3
|
require 'concurrent/delay'
|
4
4
|
|
5
5
|
module Concurrent
|
6
|
+
# @!visibility private
|
6
7
|
module Utility
|
7
8
|
|
8
9
|
# @!visibility private
|
@@ -10,70 +11,36 @@ module Concurrent
|
|
10
11
|
def initialize
|
11
12
|
@processor_count = Delay.new { compute_processor_count }
|
12
13
|
@physical_processor_count = Delay.new { compute_physical_processor_count }
|
14
|
+
@cpu_quota = Delay.new { compute_cpu_quota }
|
13
15
|
end
|
14
16
|
|
15
|
-
# Number of processors seen by the OS and used for process scheduling. For
|
16
|
-
# performance reasons the calculated value will be memoized on the first
|
17
|
-
# call.
|
18
|
-
#
|
19
|
-
# When running under JRuby the Java runtime call
|
20
|
-
# `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
|
21
|
-
# to the Java documentation this "value may change during a particular
|
22
|
-
# invocation of the virtual machine... [applications] should therefore
|
23
|
-
# occasionally poll this property." Subsequently the result will NOT be
|
24
|
-
# memoized under JRuby.
|
25
|
-
#
|
26
|
-
# Ruby's Etc.nprocessors will be used if available (MRI 2.2+).
|
27
|
-
#
|
28
|
-
# On Windows the Win32 API will be queried for the
|
29
|
-
# `NumberOfLogicalProcessors from Win32_Processor`. This will return the
|
30
|
-
# total number "logical processors for the current instance of the
|
31
|
-
# processor", which taked into account hyperthreading.
|
32
|
-
#
|
33
|
-
# * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
|
34
|
-
# * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used)
|
35
|
-
# * BSD: /sbin/sysctl
|
36
|
-
# * Cygwin: /proc/cpuinfo
|
37
|
-
# * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
|
38
|
-
# * HP-UX: /usr/sbin/ioscan
|
39
|
-
# * IRIX: /usr/sbin/sysconf
|
40
|
-
# * Linux: /proc/cpuinfo
|
41
|
-
# * Minix 3+: /proc/cpuinfo
|
42
|
-
# * Solaris: /usr/sbin/psrinfo
|
43
|
-
# * Tru64 UNIX: /usr/sbin/psrinfo
|
44
|
-
# * UnixWare: /usr/sbin/psrinfo
|
45
|
-
#
|
46
|
-
# @return [Integer] number of processors seen by the OS or Java runtime
|
47
|
-
#
|
48
|
-
# @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
|
49
|
-
#
|
50
|
-
# @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
|
51
|
-
# @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
|
52
17
|
def processor_count
|
53
18
|
@processor_count.value
|
54
19
|
end
|
55
20
|
|
56
|
-
# Number of physical processor cores on the current system. For performance
|
57
|
-
# reasons the calculated value will be memoized on the first call.
|
58
|
-
#
|
59
|
-
# On Windows the Win32 API will be queried for the `NumberOfCores from
|
60
|
-
# Win32_Processor`. This will return the total number "of cores for the
|
61
|
-
# current instance of the processor." On Unix-like operating systems either
|
62
|
-
# the `hwprefs` or `sysctl` utility will be called in a subshell and the
|
63
|
-
# returned value will be used. In the rare case where none of these methods
|
64
|
-
# work or an exception is raised the function will simply return 1.
|
65
|
-
#
|
66
|
-
# @return [Integer] number physical processor cores on the current system
|
67
|
-
#
|
68
|
-
# @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
|
69
|
-
#
|
70
|
-
# @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
|
71
|
-
# @see http://www.unix.com/man-page/osx/1/HWPREFS/
|
72
|
-
# @see http://linux.die.net/man/8/sysctl
|
73
21
|
def physical_processor_count
|
74
22
|
@physical_processor_count.value
|
75
23
|
end
|
76
24
|
|
25
|
+
def available_processor_count
|
26
|
+
cpu_count = processor_count.to_f
|
27
|
+
quota = cpu_quota
|
28
|
+
|
29
|
+
return cpu_count if quota.nil?
|
30
|
+
|
31
|
+
# cgroup cpus quotas have no limits, so they can be set to higher than the
|
32
|
+
# real count of cores.
|
33
|
+
if quota > cpu_count
|
34
|
+
cpu_count
|
35
|
+
else
|
36
|
+
quota
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def cpu_quota
|
41
|
+
@cpu_quota.value
|
42
|
+
end
|
43
|
+
|
77
44
|
private
|
78
45
|
|
79
46
|
def compute_processor_count
|
@@ -101,10 +68,20 @@ module Concurrent
|
|
101
68
|
end
|
102
69
|
cores.count
|
103
70
|
when /mswin|mingw/
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
71
|
+
# Get-CimInstance introduced in PowerShell 3 or earlier: https://learn.microsoft.com/en-us/previous-versions/powershell/module/cimcmdlets/get-ciminstance?view=powershell-3.0
|
72
|
+
result = run('powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores | Select-Object -Property NumberOfCores"')
|
73
|
+
if !result || $?.exitstatus != 0
|
74
|
+
# fallback to deprecated wmic for older systems
|
75
|
+
result = run("wmic cpu get NumberOfCores")
|
76
|
+
end
|
77
|
+
if !result || $?.exitstatus != 0
|
78
|
+
# Bail out if both commands returned something unexpected
|
79
|
+
processor_count
|
80
|
+
else
|
81
|
+
# powershell: "\nNumberOfCores\n-------------\n 4\n\n\n"
|
82
|
+
# wmic: "NumberOfCores \n\n4 \n\n\n\n"
|
83
|
+
result.scan(/\d+/).map(&:to_i).reduce(:+)
|
84
|
+
end
|
108
85
|
else
|
109
86
|
processor_count
|
110
87
|
end
|
@@ -113,6 +90,29 @@ module Concurrent
|
|
113
90
|
rescue
|
114
91
|
return 1
|
115
92
|
end
|
93
|
+
|
94
|
+
def run(command)
|
95
|
+
IO.popen(command, &:read)
|
96
|
+
rescue Errno::ENOENT
|
97
|
+
end
|
98
|
+
|
99
|
+
def compute_cpu_quota
|
100
|
+
if RbConfig::CONFIG["target_os"].include?("linux")
|
101
|
+
if File.exist?("/sys/fs/cgroup/cpu.max")
|
102
|
+
# cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
|
103
|
+
cpu_max = File.read("/sys/fs/cgroup/cpu.max")
|
104
|
+
return nil if cpu_max.start_with?("max ") # no limit
|
105
|
+
max, period = cpu_max.split.map(&:to_f)
|
106
|
+
max / period
|
107
|
+
elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
|
108
|
+
# cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
|
109
|
+
max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
|
110
|
+
return nil if max == 0
|
111
|
+
period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
|
112
|
+
max / period
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
@@ -120,11 +120,71 @@ module Concurrent
|
|
120
120
|
@processor_counter = Utility::ProcessorCounter.new
|
121
121
|
singleton_class.send :attr_reader, :processor_counter
|
122
122
|
|
123
|
+
# Number of processors seen by the OS and used for process scheduling. For
|
124
|
+
# performance reasons the calculated value will be memoized on the first
|
125
|
+
# call.
|
126
|
+
#
|
127
|
+
# When running under JRuby the Java runtime call
|
128
|
+
# `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
|
129
|
+
# to the Java documentation this "value may change during a particular
|
130
|
+
# invocation of the virtual machine... [applications] should therefore
|
131
|
+
# occasionally poll this property." Subsequently the result will NOT be
|
132
|
+
# memoized under JRuby.
|
133
|
+
#
|
134
|
+
# Otherwise Ruby's Etc.nprocessors will be used.
|
135
|
+
#
|
136
|
+
# @return [Integer] number of processors seen by the OS or Java runtime
|
137
|
+
#
|
138
|
+
# @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
|
123
139
|
def self.processor_count
|
124
140
|
processor_counter.processor_count
|
125
141
|
end
|
126
142
|
|
143
|
+
# Number of physical processor cores on the current system. For performance
|
144
|
+
# reasons the calculated value will be memoized on the first call.
|
145
|
+
#
|
146
|
+
# On Windows the Win32 API will be queried for the `NumberOfCores from
|
147
|
+
# Win32_Processor`. This will return the total number "of cores for the
|
148
|
+
# current instance of the processor." On Unix-like operating systems either
|
149
|
+
# the `hwprefs` or `sysctl` utility will be called in a subshell and the
|
150
|
+
# returned value will be used. In the rare case where none of these methods
|
151
|
+
# work or an exception is raised the function will simply return 1.
|
152
|
+
#
|
153
|
+
# @return [Integer] number physical processor cores on the current system
|
154
|
+
#
|
155
|
+
# @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
|
156
|
+
#
|
157
|
+
# @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
|
158
|
+
# @see http://www.unix.com/man-page/osx/1/HWPREFS/
|
159
|
+
# @see http://linux.die.net/man/8/sysctl
|
127
160
|
def self.physical_processor_count
|
128
161
|
processor_counter.physical_processor_count
|
129
162
|
end
|
163
|
+
|
164
|
+
# Number of processors cores available for process scheduling.
|
165
|
+
# Returns `nil` if there is no #cpu_quota, or a `Float` if the
|
166
|
+
# process is inside a cgroup with a dedicated CPU quota (typically Docker).
|
167
|
+
#
|
168
|
+
# For performance reasons the calculated value will be memoized on the first
|
169
|
+
# call.
|
170
|
+
#
|
171
|
+
# @return [nil, Float] number of available processors
|
172
|
+
def self.available_processor_count
|
173
|
+
processor_counter.available_processor_count
|
174
|
+
end
|
175
|
+
|
176
|
+
# The maximum number of processors cores available for process scheduling.
|
177
|
+
# Returns `nil` if there is no enforced limit, or a `Float` if the
|
178
|
+
# process is inside a cgroup with a dedicated CPU quota (typically Docker).
|
179
|
+
#
|
180
|
+
# Note that nothing prevents setting a CPU quota higher than the actual number of
|
181
|
+
# cores on the system.
|
182
|
+
#
|
183
|
+
# For performance reasons the calculated value will be memoized on the first
|
184
|
+
# call.
|
185
|
+
#
|
186
|
+
# @return [nil, Float] Maximum number of available processors as set by a cgroup CPU quota, or nil if none set
|
187
|
+
def self.cpu_quota
|
188
|
+
processor_counter.cpu_quota
|
189
|
+
end
|
130
190
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
- Petr Chalupa
|
9
9
|
- The Ruby Concurrency Team
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-06-09 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: |
|
16
16
|
Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
|
@@ -49,7 +49,6 @@ files:
|
|
49
49
|
- lib/concurrent-ruby/concurrent/array.rb
|
50
50
|
- lib/concurrent-ruby/concurrent/async.rb
|
51
51
|
- lib/concurrent-ruby/concurrent/atom.rb
|
52
|
-
- lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb
|
53
52
|
- lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb
|
54
53
|
- lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb
|
55
54
|
- lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb
|
@@ -57,17 +56,19 @@ files:
|
|
57
56
|
- lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb
|
58
57
|
- lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb
|
59
58
|
- lib/concurrent-ruby/concurrent/atomic/event.rb
|
59
|
+
- lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb
|
60
60
|
- lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb
|
61
|
-
- lib/concurrent-ruby/concurrent/atomic/
|
61
|
+
- lib/concurrent-ruby/concurrent/atomic/locals.rb
|
62
|
+
- lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb
|
62
63
|
- lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb
|
63
64
|
- lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb
|
64
65
|
- lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb
|
65
66
|
- lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb
|
66
67
|
- lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb
|
67
68
|
- lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb
|
68
|
-
- lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb
|
69
69
|
- lib/concurrent-ruby/concurrent/atomic/semaphore.rb
|
70
70
|
- lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb
|
71
|
+
- lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb
|
71
72
|
- lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb
|
72
73
|
- lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb
|
73
74
|
- lib/concurrent-ruby/concurrent/atomics.rb
|
@@ -75,7 +76,6 @@ files:
|
|
75
76
|
- lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb
|
76
77
|
- lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb
|
77
78
|
- lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb
|
78
|
-
- lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb
|
79
79
|
- lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb
|
80
80
|
- lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb
|
81
81
|
- lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb
|
@@ -135,21 +135,17 @@ files:
|
|
135
135
|
- lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb
|
136
136
|
- lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb
|
137
137
|
- lib/concurrent-ruby/concurrent/synchronization/condition.rb
|
138
|
+
- lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb
|
138
139
|
- lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb
|
139
|
-
- lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb
|
140
140
|
- lib/concurrent-ruby/concurrent/synchronization/lock.rb
|
141
141
|
- lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb
|
142
|
-
- lib/concurrent-ruby/concurrent/synchronization/mri_object.rb
|
143
142
|
- lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb
|
144
143
|
- lib/concurrent-ruby/concurrent/synchronization/object.rb
|
145
|
-
- lib/concurrent-ruby/concurrent/synchronization/
|
146
|
-
- lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb
|
147
|
-
- lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb
|
144
|
+
- lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb
|
148
145
|
- lib/concurrent-ruby/concurrent/synchronization/volatile.rb
|
149
146
|
- lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb
|
150
147
|
- lib/concurrent-ruby/concurrent/thread_safe/util.rb
|
151
148
|
- lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb
|
152
|
-
- lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb
|
153
149
|
- lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb
|
154
150
|
- lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb
|
155
151
|
- lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb
|
@@ -170,7 +166,7 @@ licenses:
|
|
170
166
|
metadata:
|
171
167
|
source_code_uri: https://github.com/ruby-concurrency/concurrent-ruby
|
172
168
|
changelog_uri: https://github.com/ruby-concurrency/concurrent-ruby/blob/master/CHANGELOG.md
|
173
|
-
post_install_message:
|
169
|
+
post_install_message:
|
174
170
|
rdoc_options: []
|
175
171
|
require_paths:
|
176
172
|
- lib/concurrent-ruby
|
@@ -178,15 +174,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
178
174
|
requirements:
|
179
175
|
- - ">="
|
180
176
|
- !ruby/object:Gem::Version
|
181
|
-
version: '2.
|
177
|
+
version: '2.3'
|
182
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
179
|
requirements:
|
184
180
|
- - ">="
|
185
181
|
- !ruby/object:Gem::Version
|
186
182
|
version: '0'
|
187
183
|
requirements: []
|
188
|
-
rubygems_version: 3.3.
|
189
|
-
signing_key:
|
184
|
+
rubygems_version: 3.3.26
|
185
|
+
signing_key:
|
190
186
|
specification_version: 4
|
191
187
|
summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
|
192
188
|
F#, C#, Java, and classic concurrency patterns.
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'concurrent/constants'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
# @!macro thread_local_var
|
6
|
-
# @!macro internal_implementation_note
|
7
|
-
# @!visibility private
|
8
|
-
class AbstractThreadLocalVar
|
9
|
-
|
10
|
-
# @!macro thread_local_var_method_initialize
|
11
|
-
def initialize(default = nil, &default_block)
|
12
|
-
if default && block_given?
|
13
|
-
raise ArgumentError, "Cannot use both value and block as default value"
|
14
|
-
end
|
15
|
-
|
16
|
-
if block_given?
|
17
|
-
@default_block = default_block
|
18
|
-
@default = nil
|
19
|
-
else
|
20
|
-
@default_block = nil
|
21
|
-
@default = default
|
22
|
-
end
|
23
|
-
|
24
|
-
allocate_storage
|
25
|
-
end
|
26
|
-
|
27
|
-
# @!macro thread_local_var_method_get
|
28
|
-
def value
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
# @!macro thread_local_var_method_set
|
33
|
-
def value=(value)
|
34
|
-
raise NotImplementedError
|
35
|
-
end
|
36
|
-
|
37
|
-
# @!macro thread_local_var_method_bind
|
38
|
-
def bind(value, &block)
|
39
|
-
if block_given?
|
40
|
-
old_value = self.value
|
41
|
-
begin
|
42
|
-
self.value = value
|
43
|
-
yield
|
44
|
-
ensure
|
45
|
-
self.value = old_value
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
protected
|
51
|
-
|
52
|
-
# @!visibility private
|
53
|
-
def allocate_storage
|
54
|
-
raise NotImplementedError
|
55
|
-
end
|
56
|
-
|
57
|
-
# @!visibility private
|
58
|
-
def default
|
59
|
-
if @default_block
|
60
|
-
self.value = @default_block.call
|
61
|
-
else
|
62
|
-
@default
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/abstract_thread_local_var'
|
2
|
-
|
3
|
-
if Concurrent.on_jruby?
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
# @!visibility private
|
8
|
-
# @!macro internal_implementation_note
|
9
|
-
class JavaThreadLocalVar < AbstractThreadLocalVar
|
10
|
-
|
11
|
-
# @!macro thread_local_var_method_get
|
12
|
-
def value
|
13
|
-
value = @var.get
|
14
|
-
|
15
|
-
if value.nil?
|
16
|
-
default
|
17
|
-
elsif value == NULL
|
18
|
-
nil
|
19
|
-
else
|
20
|
-
value
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# @!macro thread_local_var_method_set
|
25
|
-
def value=(value)
|
26
|
-
@var.set(value)
|
27
|
-
end
|
28
|
-
|
29
|
-
protected
|
30
|
-
|
31
|
-
# @!visibility private
|
32
|
-
def allocate_storage
|
33
|
-
@var = java.lang.ThreadLocal.new
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,181 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'concurrent/atomic/abstract_thread_local_var'
|
3
|
-
|
4
|
-
module Concurrent
|
5
|
-
|
6
|
-
# @!visibility private
|
7
|
-
# @!macro internal_implementation_note
|
8
|
-
class RubyThreadLocalVar < AbstractThreadLocalVar
|
9
|
-
|
10
|
-
# Each thread has a (lazily initialized) array of thread-local variable values
|
11
|
-
# Each time a new thread-local var is created, we allocate an "index" for it
|
12
|
-
# For example, if the allocated index is 1, that means slot #1 in EVERY
|
13
|
-
# thread's thread-local array will be used for the value of that TLV
|
14
|
-
#
|
15
|
-
# The good thing about using a per-THREAD structure to hold values, rather
|
16
|
-
# than a per-TLV structure, is that no synchronization is needed when
|
17
|
-
# reading and writing those values (since the structure is only ever
|
18
|
-
# accessed by a single thread)
|
19
|
-
#
|
20
|
-
# Of course, when a TLV is GC'd, 1) we need to recover its index for use
|
21
|
-
# by other new TLVs (otherwise the thread-local arrays could get bigger
|
22
|
-
# and bigger with time), and 2) we need to null out all the references
|
23
|
-
# held in the now-unused slots (both to avoid blocking GC of those objects,
|
24
|
-
# and also to prevent "stale" values from being passed on to a new TLV
|
25
|
-
# when the index is reused)
|
26
|
-
# Because we need to null out freed slots, we need to keep references to
|
27
|
-
# ALL the thread-local arrays -- ARRAYS is for that
|
28
|
-
# But when a Thread is GC'd, we need to drop the reference to its thread-local
|
29
|
-
# array, so we don't leak memory
|
30
|
-
|
31
|
-
FREE = []
|
32
|
-
LOCK = Mutex.new
|
33
|
-
THREAD_LOCAL_ARRAYS = {} # used as a hash set
|
34
|
-
|
35
|
-
# synchronize when not on MRI
|
36
|
-
# on MRI using lock in finalizer leads to "can't be called from trap context" error
|
37
|
-
# so the code is carefully written to be tread-safe on MRI relying on GIL
|
38
|
-
|
39
|
-
if Concurrent.on_cruby?
|
40
|
-
# @!visibility private
|
41
|
-
def self.semi_sync(&block)
|
42
|
-
block.call
|
43
|
-
end
|
44
|
-
else
|
45
|
-
# @!visibility private
|
46
|
-
def self.semi_sync(&block)
|
47
|
-
LOCK.synchronize(&block)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
|
52
|
-
|
53
|
-
# @!macro thread_local_var_method_get
|
54
|
-
def value
|
55
|
-
if (array = get_threadlocal_array)
|
56
|
-
value = array[@index]
|
57
|
-
if value.nil?
|
58
|
-
default
|
59
|
-
elsif value.equal?(NULL)
|
60
|
-
nil
|
61
|
-
else
|
62
|
-
value
|
63
|
-
end
|
64
|
-
else
|
65
|
-
default
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# @!macro thread_local_var_method_set
|
70
|
-
def value=(value)
|
71
|
-
me = Thread.current
|
72
|
-
# We could keep the thread-local arrays in a hash, keyed by Thread
|
73
|
-
# But why? That would require locking
|
74
|
-
# Using Ruby's built-in thread-local storage is faster
|
75
|
-
unless (array = get_threadlocal_array(me))
|
76
|
-
array = set_threadlocal_array([], me)
|
77
|
-
self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
|
78
|
-
ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
|
79
|
-
end
|
80
|
-
array[@index] = (value.nil? ? NULL : value)
|
81
|
-
value
|
82
|
-
end
|
83
|
-
|
84
|
-
protected
|
85
|
-
|
86
|
-
# @!visibility private
|
87
|
-
def allocate_storage
|
88
|
-
@index = FREE.pop || next_index
|
89
|
-
|
90
|
-
ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
|
91
|
-
end
|
92
|
-
|
93
|
-
# @!visibility private
|
94
|
-
def self.thread_local_finalizer(index)
|
95
|
-
proc do
|
96
|
-
semi_sync do
|
97
|
-
# The cost of GC'ing a TLV is linear in the number of threads using TLVs
|
98
|
-
# But that is natural! More threads means more storage is used per TLV
|
99
|
-
# So naturally more CPU time is required to free more storage
|
100
|
-
#
|
101
|
-
# DO NOT use each_value which might conflict with new pair assignment
|
102
|
-
# into the hash in #value= method
|
103
|
-
THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
|
104
|
-
# free index has to be published after the arrays are cleared
|
105
|
-
FREE.push(index)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# @!visibility private
|
111
|
-
def self.thread_finalizer(id)
|
112
|
-
proc do
|
113
|
-
semi_sync do
|
114
|
-
# The thread which used this thread-local array is now gone
|
115
|
-
# So don't hold onto a reference to the array (thus blocking GC)
|
116
|
-
THREAD_LOCAL_ARRAYS.delete(id)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
122
|
-
|
123
|
-
# noinspection RubyClassVariableUsageInspection
|
124
|
-
@@next = 0
|
125
|
-
# noinspection RubyClassVariableUsageInspection
|
126
|
-
def next_index
|
127
|
-
LOCK.synchronize do
|
128
|
-
result = @@next
|
129
|
-
@@next += 1
|
130
|
-
result
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
if Thread.instance_methods.include?(:thread_variable_get)
|
135
|
-
|
136
|
-
def get_threadlocal_array(thread = Thread.current)
|
137
|
-
thread.thread_variable_get(:__threadlocal_array__)
|
138
|
-
end
|
139
|
-
|
140
|
-
def set_threadlocal_array(array, thread = Thread.current)
|
141
|
-
thread.thread_variable_set(:__threadlocal_array__, array)
|
142
|
-
end
|
143
|
-
|
144
|
-
else
|
145
|
-
|
146
|
-
def get_threadlocal_array(thread = Thread.current)
|
147
|
-
thread[:__threadlocal_array__]
|
148
|
-
end
|
149
|
-
|
150
|
-
def set_threadlocal_array(array, thread = Thread.current)
|
151
|
-
thread[:__threadlocal_array__] = array
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# This exists only for use in testing
|
156
|
-
# @!visibility private
|
157
|
-
def value_for(thread)
|
158
|
-
if (array = get_threadlocal_array(thread))
|
159
|
-
value = array[@index]
|
160
|
-
if value.nil?
|
161
|
-
get_default
|
162
|
-
elsif value.equal?(NULL)
|
163
|
-
nil
|
164
|
-
else
|
165
|
-
value
|
166
|
-
end
|
167
|
-
else
|
168
|
-
get_default
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
# @!visibility private
|
173
|
-
def get_default
|
174
|
-
if @default_block
|
175
|
-
raise "Cannot use default_for with default block"
|
176
|
-
else
|
177
|
-
@default
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|