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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -1
  3. data/Gemfile +1 -2
  4. data/README.md +23 -20
  5. data/Rakefile +75 -65
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  7. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  8. data/lib/concurrent-ruby/concurrent/array.rb +3 -13
  9. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  10. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  11. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  12. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +81 -151
  14. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  15. data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  18. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  19. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  21. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  22. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
  24. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  25. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +5 -3
  26. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
  27. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  28. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  29. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  30. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  31. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  32. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  33. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
  34. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
  35. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +4 -0
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +6 -9
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +5 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +7 -0
  47. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
  48. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  49. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  50. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +6 -2
  51. data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +43 -39
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +1 -1
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  78. data/lib/concurrent-ruby/concurrent/timer_task.rb +59 -9
  79. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  80. data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
  81. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  82. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
  83. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  84. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  85. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +118 -58
  86. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  87. metadata +13 -17
  88. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  89. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  90. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  91. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
  92. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  93. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  94. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  96. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
  97. 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
- require 'win32ole'
105
- result_set = WIN32OLE.connect("winmgmts://").ExecQuery(
106
- "select NumberOfCores from Win32_Processor")
107
- result_set.to_enum.collect(&:NumberOfCores).reduce(:+)
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
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '1.1.10'
2
+ VERSION = '1.3.3'
3
3
  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.1.10
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: 2022-03-22 00:00:00.000000000 Z
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/java_thread_local_var.rb
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/rbx_lockable_object.rb
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.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.4
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