concurrent-ruby 1.2.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -2
- data/Gemfile +6 -6
- data/README.md +7 -3
- data/Rakefile +52 -28
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
- data/lib/concurrent-ruby/concurrent/agent.rb +2 -2
- data/lib/concurrent-ruby/concurrent/array.rb +3 -3
- data/lib/concurrent-ruby/concurrent/async.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +9 -2
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +15 -2
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +8 -1
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +1 -2
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
- data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
- data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +17 -12
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/delay.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +5 -7
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
- data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -3
- data/lib/concurrent-ruby/concurrent/map.rb +3 -3
- data/lib/concurrent-ruby/concurrent/mvar.rb +4 -4
- data/lib/concurrent-ruby/concurrent/promise.rb +2 -2
- data/lib/concurrent-ruby/concurrent/promises.rb +33 -23
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +1 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +116 -6
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +7 -10
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -81
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d094624e5f9f2dffc45b80892987f339d8b8c09ff2f01cf923e3825dd6409f7
|
|
4
|
+
data.tar.gz: 02adb496e81a3adb7e3c6042f72ec421677851033d57eb77b0e1b3641ef70684
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43a4af037c64d4ee8e128963db7a3f40213a45898289bb9143aa222e5664982b8c0fa08d2dae8e629fa7766be6a528b74b5ba17fbbb5cb741a9b5c08020eea15
|
|
7
|
+
data.tar.gz: 0203e085d78340af99c8d8cbf2f0aa8793db6bfdbe4c25cd2ff622816581004c2871851c621d23bcfd653be30fd3aca682e98a3117b64e5735d23bb2a3bfb4c0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
## Current
|
|
2
2
|
|
|
3
|
+
## Release v1.3.7 (16 June 2026)
|
|
4
|
+
|
|
5
|
+
concurrent-ruby:
|
|
6
|
+
|
|
7
|
+
* See the [release notes on GitHub](https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.3.7).
|
|
8
|
+
|
|
9
|
+
## Release v1.3.6 (13 December 2025)
|
|
10
|
+
|
|
11
|
+
concurrent-ruby:
|
|
12
|
+
|
|
13
|
+
* See the [release notes on GitHub](https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.3.6).
|
|
14
|
+
|
|
15
|
+
## Release v1.3.5, edge v0.7.2 (15 January 2025)
|
|
16
|
+
|
|
17
|
+
concurrent-ruby:
|
|
18
|
+
|
|
19
|
+
* (#1062) Remove dependency on logger.
|
|
20
|
+
|
|
21
|
+
concurrent-ruby-edge:
|
|
22
|
+
|
|
23
|
+
* (#1062) Remove dependency on logger.
|
|
24
|
+
|
|
25
|
+
## Release v1.3.4 (10 August 2024)
|
|
26
|
+
|
|
27
|
+
* (#1060) Fix bug with return value of `Concurrent.available_processor_count` when `cpu.cfs_quota_us` is -1.
|
|
28
|
+
* (#1058) Add `Concurrent.cpu_shares` that is cgroups aware.
|
|
29
|
+
|
|
30
|
+
## Release v1.3.3 (9 June 2024)
|
|
31
|
+
|
|
32
|
+
* (#1053) Improve the speed of `Concurrent.physical_processor_count` on Windows.
|
|
33
|
+
|
|
34
|
+
## Release v1.3.2, edge v0.7.1 (7 June 2024)
|
|
35
|
+
|
|
36
|
+
concurrent-ruby:
|
|
37
|
+
|
|
38
|
+
* (#1051) Remove dependency on `win32ole`.
|
|
39
|
+
|
|
40
|
+
concurrent-ruby-edge:
|
|
41
|
+
|
|
42
|
+
* (#1052) Fix dependency on `concurrent-ruby` to allow the latest release.
|
|
43
|
+
|
|
44
|
+
## Release v1.3.1 (29 May 2024)
|
|
45
|
+
|
|
46
|
+
* Release 1.3.0 was broken when pushed to RubyGems. 1.3.1 is a packaging fix.
|
|
47
|
+
|
|
48
|
+
## Release v1.3.0 (28 May 2024)
|
|
49
|
+
|
|
50
|
+
* (#1042) Align Java Executor Service behavior for `shuttingdown?`, `shutdown?`
|
|
51
|
+
* (#1038) Add `Concurrent.available_processor_count` that is cgroups aware.
|
|
52
|
+
|
|
53
|
+
## Release v1.2.3 (16 Jan 2024)
|
|
54
|
+
|
|
55
|
+
* See [the GitHub release](https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.2.3) for details.
|
|
56
|
+
|
|
3
57
|
## Release v1.2.2 (24 Feb 2023)
|
|
4
58
|
|
|
5
59
|
* (#993) Fix arguments passed to `Concurrent::Map`'s `default_proc`.
|
|
@@ -264,7 +318,7 @@ concurrent-ruby-edge:
|
|
|
264
318
|
* Simplification of `RubySingleThreadExecutor`
|
|
265
319
|
* `Async` improvements
|
|
266
320
|
- Each object uses its own `SingleThreadExecutor` instead of the global thread pool.
|
|
267
|
-
- No
|
|
321
|
+
- No longer supports executor injection
|
|
268
322
|
- Much better documentation
|
|
269
323
|
* `Atom` updates
|
|
270
324
|
- No longer `Dereferenceable`
|
|
@@ -439,7 +493,7 @@ Please see the [roadmap](https://github.com/ruby-concurrency/concurrent-ruby/iss
|
|
|
439
493
|
* Fixed bug with return value of `Concurrent::Actor::Utils::Pool#ask`
|
|
440
494
|
* Fixed timing bug in `TimerTask`
|
|
441
495
|
* Fixed bug when creating a `JavaThreadPoolExecutor` with minimum pool size of zero
|
|
442
|
-
* Removed confusing warning when not using native
|
|
496
|
+
* Removed confusing warning when not using native extensions
|
|
443
497
|
* Improved documentation
|
|
444
498
|
|
|
445
499
|
## Release v0.7.0 (13 August 2014)
|
data/Gemfile
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
version = File.read("#{__dir__}/lib/concurrent-ruby/concurrent/version.rb")[/'(.+)'/, 1] or raise
|
|
4
|
+
edge_version = File.read("#{__dir__}/lib/concurrent-ruby-edge/concurrent/edge/version.rb")[/'(.+)'/, 1] or raise
|
|
5
5
|
|
|
6
6
|
no_path = ENV['NO_PATH']
|
|
7
7
|
options = no_path ? {} : { path: '.' }
|
|
8
8
|
|
|
9
|
-
gem 'concurrent-ruby',
|
|
10
|
-
gem 'concurrent-ruby-edge',
|
|
11
|
-
gem 'concurrent-ruby-ext',
|
|
9
|
+
gem 'concurrent-ruby', version, options
|
|
10
|
+
gem 'concurrent-ruby-edge', edge_version, options
|
|
11
|
+
gem 'concurrent-ruby-ext', version, options.merge(platform: :mri)
|
|
12
12
|
|
|
13
13
|
group :development do
|
|
14
14
|
gem 'rake', '~> 13.0'
|
|
15
|
-
gem 'rake-compiler', '~> 1.0', '>= 1.0.7'
|
|
15
|
+
gem 'rake-compiler', '~> 1.0', '>= 1.0.7', '!= 1.2.4'
|
|
16
16
|
gem 'rake-compiler-dock', '~> 1.0'
|
|
17
17
|
gem 'pry', '~> 0.11', platforms: :mri
|
|
18
18
|
end
|
data/README.md
CHANGED
|
@@ -207,7 +207,7 @@ Deprecated features are still available and bugs are being fixed, but new featur
|
|
|
207
207
|
These are available in the `concurrent-ruby-edge` companion gem.
|
|
208
208
|
|
|
209
209
|
These features are under active development and may change frequently. They are expected not to
|
|
210
|
-
keep backward compatibility (
|
|
210
|
+
keep backward compatibility (they may also lack tests and documentation). Semantic versions will
|
|
211
211
|
be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move to
|
|
212
212
|
`concurrent-ruby` when final.
|
|
213
213
|
|
|
@@ -284,7 +284,7 @@ To use the tools in the Edge gem it must be required separately:
|
|
|
284
284
|
require 'concurrent-edge'
|
|
285
285
|
```
|
|
286
286
|
|
|
287
|
-
If the library does not behave as expected, `Concurrent.
|
|
287
|
+
If the library does not behave as expected, `Concurrent.use_simple_logger(:DEBUG)` could
|
|
288
288
|
help to reveal the problem.
|
|
289
289
|
|
|
290
290
|
## Installation
|
|
@@ -358,7 +358,8 @@ best practice is to depend on `concurrent-ruby` and let users to decide if they
|
|
|
358
358
|
* Recent CRuby
|
|
359
359
|
* JRuby, `rbenv install jruby-9.2.17.0`
|
|
360
360
|
* Set env variable `CONCURRENT_JRUBY_HOME` to point to it, e.g. `/usr/local/opt/rbenv/versions/jruby-9.2.17.0`
|
|
361
|
-
* Install Docker, required for Windows builds
|
|
361
|
+
* Install Docker or Podman, required for Windows builds
|
|
362
|
+
* If `bundle config get path` is set, use `bundle config set --local path.system true` otherwise the `gem name, path: '.'` gems won't be found (Bundler limitation).
|
|
362
363
|
|
|
363
364
|
### Publishing the Gem
|
|
364
365
|
|
|
@@ -375,7 +376,10 @@ best practice is to depend on `concurrent-ruby` and let users to decide if they
|
|
|
375
376
|
* [Benoit Daloze](https://github.com/eregon)
|
|
376
377
|
* [Matthew Draper](https://github.com/matthewd)
|
|
377
378
|
* [Rafael França](https://github.com/rafaelfranca)
|
|
379
|
+
* [Charles Oliver Nutter](https://github.com/headius)
|
|
380
|
+
* [Ben Sheldon](https://github.com/bensheldon)
|
|
378
381
|
* [Samuel Williams](https://github.com/ioquatix)
|
|
382
|
+
* [Joshua Young](https://github.com/joshuay03)
|
|
379
383
|
|
|
380
384
|
### Special Thanks to
|
|
381
385
|
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require_relative 'lib/concurrent-ruby/concurrent/utility/engine'
|
|
1
|
+
version = File.read("#{__dir__}/lib/concurrent-ruby/concurrent/version.rb")[/'(.+)'/, 1] or raise
|
|
2
|
+
edge_version = File.read("#{__dir__}/lib/concurrent-ruby-edge/concurrent/edge/version.rb")[/'(.+)'/, 1] or raise
|
|
4
3
|
|
|
5
4
|
core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec')
|
|
6
5
|
ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec')
|
|
@@ -8,14 +7,16 @@ edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.
|
|
|
8
7
|
|
|
9
8
|
require 'rake/javaextensiontask'
|
|
10
9
|
|
|
11
|
-
ENV['JRUBY_HOME'] = ENV['CONCURRENT_JRUBY_HOME'] if ENV['CONCURRENT_JRUBY_HOME'] &&
|
|
10
|
+
ENV['JRUBY_HOME'] = ENV['CONCURRENT_JRUBY_HOME'] if ENV['CONCURRENT_JRUBY_HOME'] && RUBY_ENGINE != 'jruby'
|
|
12
11
|
|
|
13
12
|
Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext|
|
|
14
13
|
ext.ext_dir = 'ext/concurrent-ruby'
|
|
15
14
|
ext.lib_dir = 'lib/concurrent-ruby/concurrent'
|
|
15
|
+
ext.source_version = '8'
|
|
16
|
+
ext.target_version = '8'
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
if RUBY_ENGINE == 'ruby'
|
|
19
20
|
require 'rake/extensiontask'
|
|
20
21
|
|
|
21
22
|
Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext|
|
|
@@ -28,6 +29,10 @@ unless Concurrent.on_jruby? || Concurrent.on_truffleruby?
|
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def which?(executable)
|
|
33
|
+
!`which #{executable} 2>/dev/null`.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
require 'rake_compiler_dock'
|
|
32
37
|
namespace :repackage do
|
|
33
38
|
desc '* with Windows fat distributions'
|
|
@@ -42,12 +47,19 @@ namespace :repackage do
|
|
|
42
47
|
Rake::Task['lib/concurrent-ruby/concurrent/concurrent_ruby.jar'].invoke
|
|
43
48
|
|
|
44
49
|
# build all gem files
|
|
50
|
+
rack_compiler_dock_kwargs = {}
|
|
51
|
+
if which?('podman') and (!which?('docker') || `docker --version`.include?('podman'))
|
|
52
|
+
# podman and only podman available, so RakeCompilerDock will use podman, otherwise it uses docker
|
|
53
|
+
rack_compiler_dock_kwargs = {
|
|
54
|
+
options: ['--privileged'], # otherwise the directory in the image is empty
|
|
55
|
+
runas: false
|
|
56
|
+
}
|
|
57
|
+
end
|
|
45
58
|
%w[x86-mingw32 x64-mingw32].each do |plat|
|
|
46
59
|
RakeCompilerDock.sh(
|
|
47
60
|
"bundle install --local && bundle exec rake native:#{plat} gem --trace",
|
|
48
61
|
platform: plat,
|
|
49
|
-
|
|
50
|
-
runas: false)
|
|
62
|
+
**rack_compiler_dock_kwargs)
|
|
51
63
|
end
|
|
52
64
|
end
|
|
53
65
|
end
|
|
@@ -57,7 +69,7 @@ require 'rubygems'
|
|
|
57
69
|
require 'rubygems/package_task'
|
|
58
70
|
|
|
59
71
|
Gem::PackageTask.new(core_gemspec) {} if core_gemspec
|
|
60
|
-
Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec &&
|
|
72
|
+
Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec && RUBY_ENGINE != 'jruby'
|
|
61
73
|
Gem::PackageTask.new(edge_gemspec) {} if edge_gemspec
|
|
62
74
|
|
|
63
75
|
CLEAN.include(
|
|
@@ -85,9 +97,9 @@ begin
|
|
|
85
97
|
task :installed do
|
|
86
98
|
Bundler.with_original_env do
|
|
87
99
|
Dir.chdir(__dir__) do
|
|
88
|
-
sh "gem install pkg/concurrent-ruby-#{
|
|
89
|
-
sh "gem install pkg/concurrent-ruby-ext-#{
|
|
90
|
-
sh "gem install pkg/concurrent-ruby-edge-#{
|
|
100
|
+
sh "gem install pkg/concurrent-ruby-#{version}.gem"
|
|
101
|
+
sh "gem install pkg/concurrent-ruby-ext-#{version}.gem" if RUBY_ENGINE == 'ruby'
|
|
102
|
+
sh "gem install pkg/concurrent-ruby-edge-#{edge_version}.gem"
|
|
91
103
|
ENV['NO_PATH'] = 'true'
|
|
92
104
|
sh 'bundle update'
|
|
93
105
|
sh 'bundle exec rake spec:ci'
|
|
@@ -117,7 +129,7 @@ rescue LoadError => e
|
|
|
117
129
|
puts 'RSpec is not installed, skipping test task definitions: ' + e.message
|
|
118
130
|
end
|
|
119
131
|
|
|
120
|
-
current_yard_version_name =
|
|
132
|
+
current_yard_version_name = version
|
|
121
133
|
|
|
122
134
|
begin
|
|
123
135
|
require 'yard'
|
|
@@ -221,6 +233,8 @@ namespace :release do
|
|
|
221
233
|
# Depends on environment of @pitr-ch
|
|
222
234
|
|
|
223
235
|
task :checks do
|
|
236
|
+
raise '$CONCURRENT_JRUBY_HOME must be set' unless ENV['CONCURRENT_JRUBY_HOME']
|
|
237
|
+
|
|
224
238
|
Dir.chdir(__dir__) do
|
|
225
239
|
sh 'test -z "$(git status --porcelain)"' do |ok, res|
|
|
226
240
|
unless ok
|
|
@@ -251,15 +265,19 @@ namespace :release do
|
|
|
251
265
|
|
|
252
266
|
desc '* test actual installed gems instead of cloned repository on MRI and JRuby'
|
|
253
267
|
task :test do
|
|
268
|
+
raise '$CONCURRENT_JRUBY_HOME must be set' unless ENV['CONCURRENT_JRUBY_HOME']
|
|
269
|
+
|
|
254
270
|
Dir.chdir(__dir__) do
|
|
255
271
|
puts "Testing with the installed gem"
|
|
256
272
|
|
|
257
273
|
Bundler.with_original_env do
|
|
258
274
|
sh 'ruby -v'
|
|
275
|
+
sh 'bundle install'
|
|
259
276
|
sh 'bundle exec rake spec:installed'
|
|
260
277
|
|
|
261
|
-
env = { "PATH" => "#{ENV
|
|
278
|
+
env = { "PATH" => "#{ENV.fetch('CONCURRENT_JRUBY_HOME')}/bin:#{ENV['PATH']}" }
|
|
262
279
|
sh env, 'ruby -v'
|
|
280
|
+
sh env, 'bundle install'
|
|
263
281
|
sh env, 'bundle exec rake spec:installed'
|
|
264
282
|
end
|
|
265
283
|
|
|
@@ -271,8 +289,8 @@ namespace :release do
|
|
|
271
289
|
task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps']
|
|
272
290
|
|
|
273
291
|
namespace :publish do
|
|
274
|
-
publish_base =
|
|
275
|
-
publish_edge =
|
|
292
|
+
publish_base = nil
|
|
293
|
+
publish_edge = nil
|
|
276
294
|
|
|
277
295
|
task :ask do
|
|
278
296
|
begin
|
|
@@ -280,8 +298,15 @@ namespace :release do
|
|
|
280
298
|
input = STDIN.gets.strip.downcase
|
|
281
299
|
end until %w(y n).include?(input)
|
|
282
300
|
exit 1 if input == 'n'
|
|
301
|
+
|
|
302
|
+
begin
|
|
303
|
+
STDOUT.puts 'Do you want to publish `concurrent-ruby`? (y/n)'
|
|
304
|
+
input = STDIN.gets.strip.downcase
|
|
305
|
+
end until %w(y n).include?(input)
|
|
306
|
+
publish_base = input == 'y'
|
|
307
|
+
|
|
283
308
|
begin
|
|
284
|
-
STDOUT.puts '
|
|
309
|
+
STDOUT.puts 'Do you want to publish `concurrent-ruby-edge`? (y/n)'
|
|
285
310
|
input = STDIN.gets.strip.downcase
|
|
286
311
|
end until %w(y n).include?(input)
|
|
287
312
|
publish_edge = input == 'y'
|
|
@@ -290,29 +315,28 @@ namespace :release do
|
|
|
290
315
|
desc '** tag HEAD with current version and push to github'
|
|
291
316
|
task :tag => :ask do
|
|
292
317
|
Dir.chdir(__dir__) do
|
|
293
|
-
sh "git tag v#{
|
|
294
|
-
sh "git push origin v#{
|
|
295
|
-
sh "git tag edge-v#{
|
|
296
|
-
sh "git push origin edge-v#{
|
|
318
|
+
sh "git tag v#{version}" if publish_base
|
|
319
|
+
sh "git push origin v#{version}" if publish_base
|
|
320
|
+
sh "git tag edge-v#{edge_version}" if publish_edge
|
|
321
|
+
sh "git push origin edge-v#{edge_version}" if publish_edge
|
|
297
322
|
end
|
|
298
323
|
end
|
|
299
324
|
|
|
300
325
|
desc '** push all *.gem files to rubygems'
|
|
301
326
|
task :rubygems => :ask do
|
|
302
327
|
Dir.chdir(__dir__) do
|
|
303
|
-
sh "gem push pkg/concurrent-ruby-#{
|
|
304
|
-
sh "gem push pkg/concurrent-ruby-edge-#{
|
|
305
|
-
sh "gem push pkg/concurrent-ruby-ext-#{
|
|
306
|
-
sh "gem push pkg/concurrent-ruby-ext-#{
|
|
307
|
-
sh "gem push pkg/concurrent-ruby-ext-#{
|
|
328
|
+
sh "gem push pkg/concurrent-ruby-#{version}.gem" if publish_base
|
|
329
|
+
sh "gem push pkg/concurrent-ruby-edge-#{edge_version}.gem" if publish_edge
|
|
330
|
+
sh "gem push pkg/concurrent-ruby-ext-#{version}.gem" if publish_base
|
|
331
|
+
sh "gem push pkg/concurrent-ruby-ext-#{version}-x64-mingw32.gem" if publish_base
|
|
332
|
+
sh "gem push pkg/concurrent-ruby-ext-#{version}-x86-mingw32.gem" if publish_base
|
|
308
333
|
end
|
|
309
334
|
end
|
|
310
335
|
|
|
311
336
|
desc '** print post release steps'
|
|
312
337
|
task :post_steps do
|
|
313
|
-
|
|
314
|
-
puts 'Manually: create a release on GitHub
|
|
315
|
-
puts 'Manually: send email same as release with relevant changelog part'
|
|
338
|
+
puts
|
|
339
|
+
puts 'Manually: create a release on GitHub, use the "Generate release notes" button'
|
|
316
340
|
puts 'Manually: tweet'
|
|
317
341
|
end
|
|
318
342
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
package com.concurrent_ruby.ext;
|
|
2
2
|
|
|
3
|
+
import static org.jruby.runtime.Visibility.PRIVATE;
|
|
4
|
+
|
|
3
5
|
import java.lang.reflect.Field;
|
|
4
6
|
import java.io.IOException;
|
|
5
|
-
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
|
6
7
|
import org.jruby.Ruby;
|
|
7
8
|
import org.jruby.RubyClass;
|
|
8
9
|
import org.jruby.RubyModule;
|
|
9
|
-
import org.jruby.RubyNumeric;
|
|
10
10
|
import org.jruby.RubyObject;
|
|
11
11
|
import org.jruby.anno.JRubyClass;
|
|
12
12
|
import org.jruby.anno.JRubyMethod;
|
|
@@ -91,15 +91,9 @@ public class AtomicReferenceLibrary implements Library {
|
|
|
91
91
|
return newValue;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
@JRubyMethod(name =
|
|
94
|
+
@JRubyMethod(name = "_compare_and_set", visibility = PRIVATE)
|
|
95
95
|
public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
|
|
96
96
|
Ruby runtime = context.runtime;
|
|
97
|
-
|
|
98
|
-
if (expectedValue instanceof RubyNumeric) {
|
|
99
|
-
// numerics are not always idempotent in Ruby, so we need to do slower logic
|
|
100
|
-
return compareAndSetNumeric(context, expectedValue, newValue);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
97
|
return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue));
|
|
104
98
|
}
|
|
105
99
|
|
|
@@ -113,35 +107,6 @@ public class AtomicReferenceLibrary implements Library {
|
|
|
113
107
|
}
|
|
114
108
|
}
|
|
115
109
|
}
|
|
116
|
-
|
|
117
|
-
private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
|
|
118
|
-
Ruby runtime = context.runtime;
|
|
119
|
-
|
|
120
|
-
// loop until:
|
|
121
|
-
// * reference CAS would succeed for same-valued objects
|
|
122
|
-
// * current and expected have different values as determined by #equals
|
|
123
|
-
while (true) {
|
|
124
|
-
IRubyObject current = reference;
|
|
125
|
-
|
|
126
|
-
if (!(current instanceof RubyNumeric)) {
|
|
127
|
-
// old value is not numeric, CAS fails
|
|
128
|
-
return runtime.getFalse();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
RubyNumeric currentNumber = (RubyNumeric)current;
|
|
132
|
-
if (!currentNumber.equals(expectedValue)) {
|
|
133
|
-
// current number does not equal expected, fail CAS
|
|
134
|
-
return runtime.getFalse();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// check that current has not changed, or else allow loop to repeat
|
|
138
|
-
boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue);
|
|
139
|
-
if (success) {
|
|
140
|
-
// value is same and did not change in interim...success
|
|
141
|
-
return runtime.getTrue();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
110
|
}
|
|
146
111
|
|
|
147
112
|
private static final class UnsafeHolder {
|
|
@@ -481,7 +481,7 @@ public class ConcurrentHashMapV8<K, V>
|
|
|
481
481
|
*
|
|
482
482
|
* Maintaining API and serialization compatibility with previous
|
|
483
483
|
* versions of this class introduces several oddities. Mainly: We
|
|
484
|
-
* leave untouched but unused constructor arguments
|
|
484
|
+
* leave untouched but unused constructor arguments referring to
|
|
485
485
|
* concurrencyLevel. We accept a loadFactor constructor argument,
|
|
486
486
|
* but apply it only to initial table capacity (which is the only
|
|
487
487
|
* time that we can guarantee to honor it.) We also declare an
|
|
@@ -484,7 +484,7 @@ public class ConcurrentHashMapV8<K, V>
|
|
|
484
484
|
*
|
|
485
485
|
* Maintaining API and serialization compatibility with previous
|
|
486
486
|
* versions of this class introduces several oddities. Mainly: We
|
|
487
|
-
* leave untouched but unused constructor arguments
|
|
487
|
+
* leave untouched but unused constructor arguments referring to
|
|
488
488
|
* concurrencyLevel. We accept a loadFactor constructor argument,
|
|
489
489
|
* but apply it only to initial table capacity (which is the only
|
|
490
490
|
* time that we can guarantee to honor it.) We also declare an
|
|
@@ -371,7 +371,7 @@ module Concurrent
|
|
|
371
371
|
# @param [Float] timeout the maximum number of seconds to wait
|
|
372
372
|
# @return [Boolean] true if all actions complete before timeout
|
|
373
373
|
#
|
|
374
|
-
# @raise [Concurrent::TimeoutError] when
|
|
374
|
+
# @raise [Concurrent::TimeoutError] when timeout is reached
|
|
375
375
|
#
|
|
376
376
|
# @!macro agent_await_warning
|
|
377
377
|
def await_for!(timeout)
|
|
@@ -477,7 +477,7 @@ module Concurrent
|
|
|
477
477
|
# @param [Array<Concurrent::Agent>] agents the Agents on which to wait
|
|
478
478
|
# @return [Boolean] true if all actions complete before timeout
|
|
479
479
|
#
|
|
480
|
-
# @raise [Concurrent::TimeoutError] when
|
|
480
|
+
# @raise [Concurrent::TimeoutError] when timeout is reached
|
|
481
481
|
# @!macro agent_await_warning
|
|
482
482
|
def await_for!(timeout, *agents)
|
|
483
483
|
raise Concurrent::TimeoutError unless await_for(timeout, *agents)
|
|
@@ -21,9 +21,9 @@ module Concurrent
|
|
|
21
21
|
# @!macro internal_implementation_note
|
|
22
22
|
ArrayImplementation = case
|
|
23
23
|
when Concurrent.on_cruby?
|
|
24
|
-
# Array is thread-safe
|
|
25
|
-
#
|
|
26
|
-
#
|
|
24
|
+
# Array is not fully thread-safe on CRuby, see
|
|
25
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
|
26
|
+
# So we will need to add synchronization here
|
|
27
27
|
::Array
|
|
28
28
|
|
|
29
29
|
when Concurrent.on_jruby?
|
|
@@ -218,7 +218,7 @@ module Concurrent
|
|
|
218
218
|
|
|
219
219
|
# @!method self.new(*args, &block)
|
|
220
220
|
#
|
|
221
|
-
#
|
|
221
|
+
# Instantiate a new object and ensure proper initialization of the
|
|
222
222
|
# synchronization mechanisms.
|
|
223
223
|
#
|
|
224
224
|
# @param [Array<Object>] args Zero or more arguments to be passed to the
|
|
@@ -113,7 +113,7 @@ module Concurrent
|
|
|
113
113
|
# @option opts [Proc] :validator (nil) Optional proc used to validate new
|
|
114
114
|
# values. It must accept one and only one argument which will be the
|
|
115
115
|
# intended new value. The validator will return true if the new value
|
|
116
|
-
# is acceptable else return false (
|
|
116
|
+
# is acceptable else return false (preferably) or raise an exception.
|
|
117
117
|
#
|
|
118
118
|
# @!macro deref_options
|
|
119
119
|
#
|
|
@@ -22,7 +22,6 @@ module Concurrent
|
|
|
22
22
|
class CAtomicReference
|
|
23
23
|
include AtomicDirectUpdate
|
|
24
24
|
include AtomicNumericCompareAndSetWrapper
|
|
25
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
26
25
|
end
|
|
27
26
|
CAtomicReference
|
|
28
27
|
when Concurrent.on_jruby?
|
|
@@ -30,14 +29,22 @@ module Concurrent
|
|
|
30
29
|
# @!macro internal_implementation_note
|
|
31
30
|
class JavaAtomicReference
|
|
32
31
|
include AtomicDirectUpdate
|
|
32
|
+
include AtomicNumericCompareAndSetWrapper
|
|
33
33
|
end
|
|
34
34
|
JavaAtomicReference
|
|
35
35
|
when Concurrent.on_truffleruby?
|
|
36
36
|
class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
|
|
37
37
|
include AtomicDirectUpdate
|
|
38
|
+
if private_method_defined?(:compare_and_set_reference)
|
|
39
|
+
alias_method :_compare_and_set, :compare_and_set_reference
|
|
40
|
+
private :_compare_and_set
|
|
41
|
+
include AtomicNumericCompareAndSetWrapper
|
|
42
|
+
else
|
|
43
|
+
# AtomicNumericCompareAndSetWrapper behavior already in TruffleRuby::AtomicReference
|
|
44
|
+
alias_method :compare_and_swap, :compare_and_set
|
|
45
|
+
end
|
|
38
46
|
alias_method :value, :get
|
|
39
47
|
alias_method :value=, :set
|
|
40
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
41
48
|
alias_method :swap, :get_and_set
|
|
42
49
|
end
|
|
43
50
|
TruffleRubyAtomicReference
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'thread'
|
|
2
2
|
require 'concurrent/atomic/atomic_fixnum'
|
|
3
|
+
require 'concurrent/atomic/atomic_reference'
|
|
3
4
|
require 'concurrent/errors'
|
|
4
5
|
require 'concurrent/synchronization/object'
|
|
5
6
|
require 'concurrent/synchronization/lock'
|
|
@@ -58,7 +59,8 @@ module Concurrent
|
|
|
58
59
|
# Create a new `ReadWriteLock` in the unlocked state.
|
|
59
60
|
def initialize
|
|
60
61
|
super()
|
|
61
|
-
@Counter = AtomicFixnum.new(0)
|
|
62
|
+
@Counter = AtomicFixnum.new(0) # single integer which represents lock state
|
|
63
|
+
@Writer = AtomicReference.new(nil) # the thread currently holding the write lock
|
|
62
64
|
@ReadLock = Synchronization::Lock.new
|
|
63
65
|
@WriteLock = Synchronization::Lock.new
|
|
64
66
|
end
|
|
@@ -137,9 +139,13 @@ module Concurrent
|
|
|
137
139
|
# Release a previously acquired read lock.
|
|
138
140
|
#
|
|
139
141
|
# @return [Boolean] true if the lock is successfully released
|
|
142
|
+
#
|
|
143
|
+
# @raise [Concurrent::IllegalOperationError] if no read lock is currently held.
|
|
140
144
|
def release_read_lock
|
|
141
145
|
while true
|
|
142
146
|
c = @Counter.value
|
|
147
|
+
raise IllegalOperationError, 'Cannot release a read lock which is not held' if running_readers(c) == 0
|
|
148
|
+
|
|
143
149
|
if @Counter.compare_and_set(c, c-1)
|
|
144
150
|
# If one or more writers were waiting, and we were the last reader, wake a writer up
|
|
145
151
|
if waiting_writer?(c) && running_readers(c) == 1
|
|
@@ -187,14 +193,21 @@ module Concurrent
|
|
|
187
193
|
break
|
|
188
194
|
end
|
|
189
195
|
end
|
|
196
|
+
@Writer.set(Thread.current)
|
|
190
197
|
true
|
|
191
198
|
end
|
|
192
199
|
|
|
193
200
|
# Release a previously acquired write lock.
|
|
194
201
|
#
|
|
195
202
|
# @return [Boolean] true if the lock is successfully released
|
|
203
|
+
#
|
|
204
|
+
# @raise [Concurrent::IllegalOperationError] if the write lock is not held
|
|
205
|
+
# by the current thread.
|
|
196
206
|
def release_write_lock
|
|
197
|
-
|
|
207
|
+
unless @Writer.compare_and_set(Thread.current, nil)
|
|
208
|
+
raise IllegalOperationError, 'Cannot release a write lock which is not held by the current thread'
|
|
209
|
+
end
|
|
210
|
+
|
|
198
211
|
c = @Counter.update { |counter| counter - RUNNING_WRITER }
|
|
199
212
|
@ReadLock.broadcast
|
|
200
213
|
@WriteLock.signal if waiting_writers(c) > 0
|
|
@@ -158,9 +158,11 @@ module Concurrent
|
|
|
158
158
|
# @return [Boolean] true if the lock is successfully acquired
|
|
159
159
|
#
|
|
160
160
|
# @raise [Concurrent::ResourceLimitError] if the maximum number of readers
|
|
161
|
-
# is exceeded.
|
|
161
|
+
# or per-thread reentrant acquires is exceeded.
|
|
162
162
|
def acquire_read_lock
|
|
163
163
|
if (held = @HeldCount.value) > 0
|
|
164
|
+
raise ResourceLimitError.new('Too many reader holds on this thread') if (held & READ_LOCK_MASK) == READ_LOCK_MASK
|
|
165
|
+
|
|
164
166
|
# If we already have a lock, there's no need to wait
|
|
165
167
|
if held & READ_LOCK_MASK == 0
|
|
166
168
|
# But we do need to update the counter, if we were holding a write
|
|
@@ -212,8 +214,13 @@ module Concurrent
|
|
|
212
214
|
# acquired immediately, return false.
|
|
213
215
|
#
|
|
214
216
|
# @return [Boolean] true if the lock is successfully acquired
|
|
217
|
+
#
|
|
218
|
+
# @raise [Concurrent::ResourceLimitError] if the maximum number of per-thread
|
|
219
|
+
# reentrant acquires is exceeded.
|
|
215
220
|
def try_read_lock
|
|
216
221
|
if (held = @HeldCount.value) > 0
|
|
222
|
+
raise ResourceLimitError.new('Too many reader holds on this thread') if (held & READ_LOCK_MASK) == READ_LOCK_MASK
|
|
223
|
+
|
|
217
224
|
if held & READ_LOCK_MASK == 0
|
|
218
225
|
# If we hold a write lock, but not a read lock...
|
|
219
226
|
@Counter.update { |c| c + 1 }
|
|
@@ -10,7 +10,6 @@ module Concurrent
|
|
|
10
10
|
extend Concurrent::Synchronization::SafeInitialization
|
|
11
11
|
include AtomicDirectUpdate
|
|
12
12
|
include AtomicNumericCompareAndSetWrapper
|
|
13
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
14
13
|
|
|
15
14
|
# @!macro atomic_reference_method_initialize
|
|
16
15
|
def initialize(value = nil)
|
|
@@ -42,7 +41,7 @@ module Concurrent
|
|
|
42
41
|
alias_method :swap, :get_and_set
|
|
43
42
|
|
|
44
43
|
# @!macro atomic_reference_method_compare_and_set
|
|
45
|
-
def _compare_and_set(old_value, new_value)
|
|
44
|
+
private def _compare_and_set(old_value, new_value)
|
|
46
45
|
synchronize do
|
|
47
46
|
if @value.equal? old_value
|
|
48
47
|
@value = new_value
|
|
@@ -9,12 +9,18 @@ module Concurrent
|
|
|
9
9
|
# @!macro atomic_reference_method_compare_and_set
|
|
10
10
|
def compare_and_set(old_value, new_value)
|
|
11
11
|
if old_value.kind_of? Numeric
|
|
12
|
+
# NaN is never == to itself; match it explicitly so #update can terminate.
|
|
13
|
+
expected_nan = old_value.respond_to?(:nan?) && old_value.nan?
|
|
12
14
|
while true
|
|
13
15
|
old = get
|
|
14
16
|
|
|
15
17
|
return false unless old.kind_of? Numeric
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
if expected_nan
|
|
20
|
+
return false unless old.respond_to?(:nan?) && old.nan?
|
|
21
|
+
else
|
|
22
|
+
return false unless old == old_value
|
|
23
|
+
end
|
|
18
24
|
|
|
19
25
|
result = _compare_and_set(old, new_value)
|
|
20
26
|
return result if result
|
|
@@ -24,5 +30,7 @@ module Concurrent
|
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
alias_method :compare_and_swap, :compare_and_set
|
|
34
|
+
|
|
27
35
|
end
|
|
28
36
|
end
|