concurrent-ruby 1.1.10 → 1.3.7

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -3
  3. data/Gemfile +6 -7
  4. data/README.md +28 -23
  5. data/Rakefile +90 -76
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
  10. data/lib/concurrent-ruby/concurrent/agent.rb +4 -3
  11. data/lib/concurrent-ruby/concurrent/array.rb +3 -13
  12. data/lib/concurrent-ruby/concurrent/async.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atom.rb +2 -2
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +90 -153
  18. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  19. data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
  20. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +29 -0
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  27. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +17 -3
  29. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +13 -4
  30. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
  31. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  33. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +16 -6
  34. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
  35. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  36. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  37. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  38. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
  39. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
  40. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
  41. data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
  42. data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
  43. data/lib/concurrent-ruby/concurrent/concern/logging.rb +95 -6
  44. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  45. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  46. data/lib/concurrent-ruby/concurrent/delay.rb +3 -3
  47. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  48. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  49. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
  51. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +7 -9
  52. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
  53. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
  54. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
  55. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  58. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
  60. data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
  61. data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
  62. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  63. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  64. data/lib/concurrent-ruby/concurrent/map.rb +43 -39
  65. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  66. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  67. data/lib/concurrent-ruby/concurrent/mvar.rb +5 -5
  68. data/lib/concurrent-ruby/concurrent/promise.rb +3 -3
  69. data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
  70. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  71. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +2 -2
  72. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  73. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  74. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  75. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  76. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
  77. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  78. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  79. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  80. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  81. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
  82. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  83. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +13 -45
  84. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  85. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  86. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  87. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  88. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
  89. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  90. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
  91. data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
  92. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  93. data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
  94. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  95. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
  96. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  97. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  98. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +148 -58
  99. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  100. metadata +14 -19
  101. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  102. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  103. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  104. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
  105. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  106. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  107. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  108. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  109. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
  110. 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,41 @@ 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 }
15
+ @cpu_shares = Delay.new { compute_cpu_shares }
13
16
  end
14
17
 
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
18
  def processor_count
53
19
  @processor_count.value
54
20
  end
55
21
 
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
22
  def physical_processor_count
74
23
  @physical_processor_count.value
75
24
  end
76
25
 
26
+ def available_processor_count
27
+ cpu_count = processor_count.to_f
28
+ quota = cpu_quota
29
+
30
+ return cpu_count if quota.nil?
31
+
32
+ # cgroup cpus quotas have no limits, so they can be set to higher than the
33
+ # real count of cores.
34
+ if quota > cpu_count
35
+ cpu_count
36
+ else
37
+ quota
38
+ end
39
+ end
40
+
41
+ def cpu_quota
42
+ @cpu_quota.value
43
+ end
44
+
45
+ def cpu_shares
46
+ @cpu_shares.value
47
+ end
48
+
77
49
  private
78
50
 
79
51
  def compute_processor_count
@@ -101,10 +73,20 @@ module Concurrent
101
73
  end
102
74
  cores.count
103
75
  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(:+)
76
+ # 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
77
+ result = run('powershell -command "Get-CimInstance -ClassName Win32_Processor -Property NumberOfCores | Select-Object -Property NumberOfCores"')
78
+ if !result || $?.exitstatus != 0
79
+ # fallback to deprecated wmic for older systems
80
+ result = run("wmic cpu get NumberOfCores")
81
+ end
82
+ if !result || $?.exitstatus != 0
83
+ # Bail out if both commands returned something unexpected
84
+ processor_count
85
+ else
86
+ # powershell: "\nNumberOfCores\n-------------\n 4\n\n\n"
87
+ # wmic: "NumberOfCores \n\n4 \n\n\n\n"
88
+ result.scan(/\d+/).map(&:to_i).reduce(:+)
89
+ end
108
90
  else
109
91
  processor_count
110
92
  end
@@ -113,6 +95,45 @@ module Concurrent
113
95
  rescue
114
96
  return 1
115
97
  end
98
+
99
+ def run(command)
100
+ IO.popen(command, &:read)
101
+ rescue Errno::ENOENT
102
+ end
103
+
104
+ def compute_cpu_quota
105
+ if RbConfig::CONFIG["target_os"].include?("linux")
106
+ if File.exist?("/sys/fs/cgroup/cpu.max")
107
+ # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
108
+ cpu_max = File.read("/sys/fs/cgroup/cpu.max")
109
+ return nil if cpu_max.start_with?("max ") # no limit
110
+ max, period = cpu_max.split.map(&:to_f)
111
+ max / period
112
+ elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us")
113
+ # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
114
+ max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i
115
+ # If the cpu.cfs_quota_us is -1, cgroup does not adhere to any CPU time restrictions
116
+ # https://docs.kernel.org/scheduler/sched-bwc.html#management
117
+ return nil if max <= 0
118
+ period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f
119
+ max / period
120
+ end
121
+ end
122
+ end
123
+
124
+ def compute_cpu_shares
125
+ if RbConfig::CONFIG["target_os"].include?("linux")
126
+ if File.exist?("/sys/fs/cgroup/cpu.weight")
127
+ # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
128
+ # Ref: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2254-cgroup-v2#phase-1-convert-from-cgroups-v1-settings-to-v2
129
+ weight = File.read("/sys/fs/cgroup/cpu.weight").to_f
130
+ ((((weight - 1) * 262142) / 9999) + 2) / 1024
131
+ elsif File.exist?("/sys/fs/cgroup/cpu/cpu.shares")
132
+ # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
133
+ File.read("/sys/fs/cgroup/cpu/cpu.shares").to_f / 1024
134
+ end
135
+ end
136
+ end
116
137
  end
117
138
  end
118
139
 
@@ -120,11 +141,80 @@ module Concurrent
120
141
  @processor_counter = Utility::ProcessorCounter.new
121
142
  singleton_class.send :attr_reader, :processor_counter
122
143
 
144
+ # Number of processors seen by the OS and used for process scheduling. For
145
+ # performance reasons the calculated value will be memoized on the first
146
+ # call.
147
+ #
148
+ # When running under JRuby the Java runtime call
149
+ # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According
150
+ # to the Java documentation this "value may change during a particular
151
+ # invocation of the virtual machine... [applications] should therefore
152
+ # occasionally poll this property." We still memoize this value once under
153
+ # JRuby.
154
+ #
155
+ # Otherwise Ruby's Etc.nprocessors will be used.
156
+ #
157
+ # @return [Integer] number of processors seen by the OS or Java runtime
158
+ #
159
+ # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors()
123
160
  def self.processor_count
124
161
  processor_counter.processor_count
125
162
  end
126
163
 
164
+ # Number of physical processor cores on the current system. For performance
165
+ # reasons the calculated value will be memoized on the first call.
166
+ #
167
+ # On Windows the Win32 API will be queried for the `NumberOfCores from
168
+ # Win32_Processor`. This will return the total number "of cores for the
169
+ # current instance of the processor." On Unix-like operating systems either
170
+ # the `hwprefs` or `sysctl` utility will be called in a subshell and the
171
+ # returned value will be used. In the rare case where none of these methods
172
+ # work or an exception is raised the function will simply return 1.
173
+ #
174
+ # @return [Integer] number physical processor cores on the current system
175
+ #
176
+ # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb
177
+ #
178
+ # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx
179
+ # @see http://www.unix.com/man-page/osx/1/HWPREFS/
180
+ # @see http://linux.die.net/man/8/sysctl
127
181
  def self.physical_processor_count
128
182
  processor_counter.physical_processor_count
129
183
  end
184
+
185
+ # Number of processors cores available for process scheduling.
186
+ # This method takes in account the CPU quota if the process is inside a cgroup with a
187
+ # dedicated CPU quota (typically Docker).
188
+ # Otherwise it returns the same value as #processor_count but as a Float.
189
+ #
190
+ # For performance reasons the calculated value will be memoized on the first
191
+ # call.
192
+ #
193
+ # @return [Float] number of available processors
194
+ def self.available_processor_count
195
+ processor_counter.available_processor_count
196
+ end
197
+
198
+ # The maximum number of processors cores available for process scheduling.
199
+ # Returns `nil` if there is no enforced limit, or a `Float` if the
200
+ # process is inside a cgroup with a dedicated CPU quota (typically Docker).
201
+ #
202
+ # Note that nothing prevents setting a CPU quota higher than the actual number of
203
+ # cores on the system.
204
+ #
205
+ # For performance reasons the calculated value will be memoized on the first
206
+ # call.
207
+ #
208
+ # @return [nil, Float] Maximum number of available processors as set by a cgroup CPU quota, or nil if none set
209
+ def self.cpu_quota
210
+ processor_counter.cpu_quota
211
+ end
212
+
213
+ # The CPU shares requested by the process. For performance reasons the calculated
214
+ # value will be memoized on the first call.
215
+ #
216
+ # @return [Float, nil] CPU shares requested by the process, or nil if not set
217
+ def self.cpu_shares
218
+ processor_counter.cpu_shares
219
+ end
130
220
  end
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '1.1.10'
2
+ VERSION = '1.3.7'
3
3
  end
metadata CHANGED
@@ -1,16 +1,15 @@
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.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
8
8
  - Petr Chalupa
9
9
  - The Ruby Concurrency Team
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2022-03-22 00:00:00.000000000 Z
12
+ date: 2026-06-16 00:00:00.000000000 Z
14
13
  dependencies: []
15
14
  description: |
16
15
  Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
@@ -19,9 +18,9 @@ email: concurrent-ruby@googlegroups.com
19
18
  executables: []
20
19
  extensions: []
21
20
  extra_rdoc_files:
22
- - README.md
23
- - LICENSE.txt
24
21
  - CHANGELOG.md
22
+ - LICENSE.txt
23
+ - README.md
25
24
  files:
26
25
  - CHANGELOG.md
27
26
  - Gemfile
@@ -49,7 +48,6 @@ files:
49
48
  - lib/concurrent-ruby/concurrent/array.rb
50
49
  - lib/concurrent-ruby/concurrent/async.rb
51
50
  - lib/concurrent-ruby/concurrent/atom.rb
52
- - lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb
53
51
  - lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb
54
52
  - lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb
55
53
  - lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb
@@ -57,17 +55,19 @@ files:
57
55
  - lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb
58
56
  - lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb
59
57
  - lib/concurrent-ruby/concurrent/atomic/event.rb
58
+ - lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb
60
59
  - lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb
61
- - lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb
60
+ - lib/concurrent-ruby/concurrent/atomic/locals.rb
61
+ - lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb
62
62
  - lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb
63
63
  - lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb
64
64
  - lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb
65
65
  - lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb
66
66
  - lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb
67
67
  - lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb
68
- - lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb
69
68
  - lib/concurrent-ruby/concurrent/atomic/semaphore.rb
70
69
  - lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb
70
+ - lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb
71
71
  - lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb
72
72
  - lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb
73
73
  - lib/concurrent-ruby/concurrent/atomics.rb
@@ -75,13 +75,14 @@ files:
75
75
  - lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb
76
76
  - lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb
77
77
  - lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb
78
- - lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb
79
78
  - lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb
80
79
  - lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb
81
80
  - lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb
82
81
  - lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb
83
82
  - lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb
84
83
  - lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb
84
+ - lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb
85
+ - lib/concurrent-ruby/concurrent/collection/timeout_queue.rb
85
86
  - lib/concurrent-ruby/concurrent/concern/deprecation.rb
86
87
  - lib/concurrent-ruby/concurrent/concern/dereferenceable.rb
87
88
  - lib/concurrent-ruby/concurrent/concern/logging.rb
@@ -135,21 +136,17 @@ files:
135
136
  - lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb
136
137
  - lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb
137
138
  - lib/concurrent-ruby/concurrent/synchronization/condition.rb
139
+ - lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb
138
140
  - lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb
139
- - lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb
140
141
  - lib/concurrent-ruby/concurrent/synchronization/lock.rb
141
142
  - lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb
142
- - lib/concurrent-ruby/concurrent/synchronization/mri_object.rb
143
143
  - lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb
144
144
  - 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
145
+ - lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb
148
146
  - lib/concurrent-ruby/concurrent/synchronization/volatile.rb
149
147
  - lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb
150
148
  - lib/concurrent-ruby/concurrent/thread_safe/util.rb
151
149
  - lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb
152
- - lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb
153
150
  - lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb
154
151
  - lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb
155
152
  - lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb
@@ -170,7 +167,6 @@ licenses:
170
167
  metadata:
171
168
  source_code_uri: https://github.com/ruby-concurrency/concurrent-ruby
172
169
  changelog_uri: https://github.com/ruby-concurrency/concurrent-ruby/blob/master/CHANGELOG.md
173
- post_install_message:
174
170
  rdoc_options: []
175
171
  require_paths:
176
172
  - lib/concurrent-ruby
@@ -178,15 +174,14 @@ 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: 4.0.6
190
185
  specification_version: 4
191
186
  summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
192
187
  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