concurrent-ruby 1.1.7 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/Gemfile +2 -7
  4. data/README.md +40 -20
  5. data/Rakefile +31 -34
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  9. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +1 -0
  10. data/lib/concurrent-ruby/concurrent/atomic/event.rb +2 -2
  11. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +18 -2
  12. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +4 -6
  13. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +4 -1
  14. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +26 -5
  15. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  16. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  17. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  18. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +16 -13
  19. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  20. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +1 -1
  21. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  22. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  23. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  24. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +5 -5
  25. data/lib/concurrent-ruby/concurrent/map.rb +13 -4
  26. data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
  27. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +29 -16
  28. data/lib/concurrent-ruby/concurrent/set.rb +14 -6
  29. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +1 -3
  30. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
  31. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
  32. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
  33. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  34. data/lib/concurrent-ruby/concurrent/tvar.rb +19 -56
  35. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +67 -35
  36. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +2 -35
  37. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  38. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  39. metadata +8 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3db57904f9a4906ae01e04d1873aa824992ed1b4258d2b2523ba5ea0ca86b17f
4
- data.tar.gz: d8bfd56f2c29b311e528754ee1db476ecbb6387ac7f05a63bb5e395877379452
3
+ metadata.gz: e2508d8671dc3f93a6d41204a69a2444669688ac1e9e7790104162ac5180579e
4
+ data.tar.gz: 5eba3636194c783d376ed010bd3d6ec8796fe409870e07a84c6b0b0ea2704b41
5
5
  SHA512:
6
- metadata.gz: b76f7f31795dce80951ea7b5231d919aa28384d04fe6591c43e310774450f16b074ce5536ef218fd2475f5413cdf0c9409bc1d3542b19b78146cc5375c30a603
7
- data.tar.gz: 9bb9d431f6995bdec6e7ed6c6323f72ac0cc713d71072115c0dfcefb6ccb42c8b5b703bf34049071bebf4e0686dce2e28cc44865329884785eae8a6a2eabd060
6
+ metadata.gz: 4c1cbc5311e939aecda5e291bb579a690807de5240bb2fb30600a9d1d9de8c353558de7d6e3e0dff871fcca364bd7caa76b304428e0fdaba323cbe04be300056
7
+ data.tar.gz: 863635cad877062864813b9ba72685b3afdadabf258a62b36f8d7093a5be9c7115d64aade01c9a98b9e68ef86dff2698e5e0cef7766b9c4dfc740e0e76eccf0c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  ## Current
2
2
 
3
+ ## Release v1.1.10
4
+
5
+ concurrent-ruby:
6
+
7
+ * (#951) Set the Ruby compatibility version at 2.2
8
+ * (#939, #933) The `caller_runs` fallback policy no longer blocks reads from the job queue by worker threads
9
+ * (#938, #761, #652) You can now explicitly `prune_pool` a thread pool (Sylvain Joyeux)
10
+ * (#937, #757, #670) We switched the Yahoo stock API for demos to Alpha Vantage (Gustavo Caso)
11
+ * (#932, #931) We changed how `SafeTaskExecutor` handles local jump errors (Aaron Jensen)
12
+ * (#927) You can use keyword arguments in your initialize when using `Async` (Matt Larraz)
13
+ * (#926, #639) We removed timeout from `TimerTask` because it wasn't sound, and now it's a no-op with a warning (Jacob Atzen)
14
+ * (#919) If you double-lock a re-entrant read-write lock, we promote to locked for writing (zp yuan)
15
+ * (#915) `monotonic_time` now accepts an optional unit parameter, as Ruby's `clock_gettime` (Jean Boussier)
16
+
17
+ ## Release v1.1.9 (5 Jun 2021)
18
+
19
+ concurrent-ruby:
20
+
21
+ * (#866) Child promise state not set to :pending immediately after #execute when parent has completed
22
+ * (#905, #872) Fix RubyNonConcurrentPriorityQueue#delete method
23
+ * (2df0337d) Make sure locks are not shared on shared when objects are dup/cloned
24
+ * (#900, #906, #796, #847, #911) Fix Concurrent::Set tread-safety issues on CRuby
25
+ * (#907) Add new ConcurrentMap backend for TruffleRuby
26
+
27
+ ## Release v1.1.8 (20 January 2021)
28
+
29
+ concurrent-ruby:
30
+
31
+ * (#885) Fix race condition in TVar for stale reads
32
+ * (#884) RubyThreadLocalVar: Do not iterate over hash which might conflict with new pair addition
33
+
3
34
  ## Release v1.1.7 (6 August 2020)
4
35
 
5
36
  concurrent-ruby:
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ gem 'concurrent-ruby-edge', Concurrent::EDGE_VERSION, options
12
12
  gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri)
13
13
 
14
14
  group :development do
15
- gem 'rake', (Concurrent.ruby_version :<, 2, 2, 0) ? '~> 12.0' : '~> 13.0'
15
+ gem 'rake', '~> 13.0'
16
16
  gem 'rake-compiler', '~> 1.0', '>= 1.0.7'
17
17
  gem 'rake-compiler-dock', '~> 1.0'
18
18
  gem 'pry', '~> 0.11', platforms: :mri
@@ -26,7 +26,7 @@ end
26
26
 
27
27
  group :testing do
28
28
  gem 'rspec', '~> 3.7'
29
- gem 'timecop', '~> 0.7.4'
29
+ gem 'timecop', '~> 0.9'
30
30
  gem 'sigdump', require: false
31
31
  end
32
32
 
@@ -35,8 +35,3 @@ group :coverage, optional: !ENV['COVERAGE'] do
35
35
  gem 'simplecov', '~> 0.16.0', require: false
36
36
  gem 'coveralls', '~> 0.8.2', require: false
37
37
  end
38
-
39
- group :benchmarks, optional: true do
40
- gem 'benchmark-ips', '~> 2.7'
41
- gem 'bench9000'
42
- end
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Concurrent Ruby
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby)
4
- [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby)
5
- [![Build status](https://ci.appveyor.com/api/projects/status/iq8aboyuu3etad4w?svg=true)](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby)
6
4
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT)
7
5
  [![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby)
8
6
 
@@ -39,6 +37,8 @@ The design goals of this gem are:
39
37
  appreciate your help. Would you like to contribute? Great! Have a look at
40
38
  [issues with `looking-for-contributor` label](https://github.com/ruby-concurrency/concurrent-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Alooking-for-contributor).** And if you pick something up let us know on the issue.
41
39
 
40
+ You can also get started by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to concurrent-ruby on CodeTriage](https://www.codetriage.com/ruby-concurrency/concurrent-ruby). [![Open Source Helpers](https://www.codetriage.com/ruby-concurrency/concurrent-ruby/badges/users.svg)](https://www.codetriage.com/ruby-concurrency/concurrent-ruby)
41
+
42
42
  ## Thread Safety
43
43
 
44
44
  *Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency
@@ -259,15 +259,11 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
259
259
 
260
260
  ## Supported Ruby versions
261
261
 
262
- * MRI 2.0 and above
263
- * JRuby 9000
264
- * TruffleRuby are supported.
265
- * Any Ruby interpreter that is compliant with Ruby 2.0 or newer.
266
-
267
- Actually we still support mri 1.9.3 and jruby 1.7.27 but we are looking at ways how to drop the support.
268
- Java 8 is preferred for JRuby but every Java version on which JRuby 9000 runs is supported.
262
+ * MRI 2.2 and above
263
+ * Latest JRuby 9000
264
+ * Latest TruffleRuby
269
265
 
270
- The legacy support for Rubinius is kept but it is no longer maintained, if you would like to help
266
+ The legacy support for Rubinius is kept for the moment but it is no longer maintained and is liable to be removed. If you would like to help
271
267
  please respond to [#739](https://github.com/ruby-concurrency/concurrent-ruby/issues/739).
272
268
 
273
269
  ## Usage
@@ -353,23 +349,47 @@ and load the appropriate C extensions.
353
349
  No gems should depend on `concurrent-ruby-ext`. Doing so will force C extensions on your users. The
354
350
  best practice is to depend on `concurrent-ruby` and let users to decide if they want C extensions.
355
351
 
352
+ ## Building the gem
353
+
354
+ ### Requirements
355
+
356
+ * Recent CRuby
357
+ * JRuby, `rbenv install jruby-9.2.17.0`
358
+ * Set env variable `CONCURRENT_JRUBY_HOME` to point to it, e.g. `/usr/local/opt/rbenv/versions/jruby-9.2.17.0`
359
+ * Install Docker, required for Windows builds
360
+
361
+ ### Publishing the Gem
362
+
363
+ * Update `version.rb`
364
+ * Update the CHANGELOG
365
+ * Update the Yard documentation
366
+ - Add the new version to `docs-source/signpost.md`. Needs to be done only if there are visible changes in the
367
+ documentation.
368
+ - Run `bundle exec rake yard` to update the master documentation and signpost.
369
+ - Run `bundle exec rake yard:<new-version>` to add or update the documentation of the new version.
370
+ * Commit (and push) the changes.
371
+ * Use `be rake release` to release the gem. It consists
372
+ of `['release:checks', 'release:build', 'release:test', 'release:publish']` steps. It will ask at the end before
373
+ publishing anything. Steps can also be executed individually.
374
+
356
375
  ## Maintainers
357
376
 
358
- * [Petr Chalupa](https://github.com/pitr-ch) (lead maintainer, point-of-contact)
359
- * [Jerry D'Antonio](https://github.com/jdantonio) (creator)
360
- * [Chris Seaton](https://github.com/chrisseaton)
377
+ * [Chris Seaton](https://github.com/chrisseaton) Lead maintainer, point-of-contact.
378
+ * [Benoit Daloze](https://github.com/eregon) — If Chris is not available Benoit can help.
361
379
 
362
380
  ### Special Thanks to
363
381
 
364
- * [Brian Durand](https://github.com/bdurand) for the `ref` gem
365
- * [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems
366
- * [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem
382
+ * [Jerry D'Antonio](https://github.com/jdantonio) for creating the gem
383
+ * [Brian Durand](https://github.com/bdurand) for the `ref` gem
384
+ * [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems
385
+ * [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem
367
386
 
368
- and to the past maintainers
387
+ to the past maintainers
369
388
 
370
- * [Michele Della Torre](https://github.com/mighe)
371
- * [Paweł Obrok](https://github.com/obrok)
372
- * [Lucas Allan](https://github.com/lucasallan)
389
+ * [Petr Chalupa](https://github.com/pitr-ch)
390
+ * [Michele Della Torre](https://github.com/mighe)
391
+ * [Paweł Obrok](https://github.com/obrok)
392
+ * [Lucas Allan](https://github.com/lucasallan)
373
393
 
374
394
  and to [Ruby Association](https://www.ruby.or.jp/en/) for sponsoring a project
375
395
  ["Enhancing Ruby’s concurrency tooling"](https://www.ruby.or.jp/en/news/20181106) in 2018.
data/Rakefile CHANGED
@@ -2,15 +2,6 @@ require_relative 'lib/concurrent-ruby/concurrent/version'
2
2
  require_relative 'lib/concurrent-ruby-edge/concurrent/edge/version'
3
3
  require_relative 'lib/concurrent-ruby/concurrent/utility/engine'
4
4
 
5
- if Concurrent.ruby_version :<, 2, 0, 0
6
- # @!visibility private
7
- module Kernel
8
- def __dir__
9
- File.dirname __FILE__
10
- end
11
- end
12
- end
13
-
14
5
  core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec')
15
6
  ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec')
16
7
  edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec')
@@ -24,7 +15,7 @@ Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext|
24
15
  ext.lib_dir = 'lib/concurrent-ruby/concurrent'
25
16
  end
26
17
 
27
- unless Concurrent.on_jruby?
18
+ unless Concurrent.on_jruby? || Concurrent.on_truffleruby?
28
19
  require 'rake/extensiontask'
29
20
 
30
21
  Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext|
@@ -49,7 +40,9 @@ namespace :repackage do
49
40
  Rake::Task['lib/concurrent-ruby/concurrent/concurrent_ruby.jar'].invoke
50
41
 
51
42
  # build all gem files
52
- RakeCompilerDock.sh 'bundle install --local && bundle exec rake cross native package --trace'
43
+ %w[x86-mingw32 x64-mingw32].each do |plat|
44
+ RakeCompilerDock.sh "bundle install --local && bundle exec rake native:#{plat} gem --trace", platform: plat
45
+ end
53
46
  end
54
47
  end
55
48
  end
@@ -75,8 +68,7 @@ begin
75
68
  options = %w[ --color
76
69
  --backtrace
77
70
  --order defined
78
- --format documentation
79
- --tag ~notravis ]
71
+ --format documentation ]
80
72
  t.rspec_opts = [*options].join(' ')
81
73
  end
82
74
 
@@ -133,7 +125,7 @@ begin
133
125
  task :update_readme do
134
126
  Dir.chdir __dir__ do
135
127
  content = File.read(File.join('README.md')).
136
- gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
128
+ gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
137
129
  case $1
138
130
  when 'LockFreeLinkedSet'
139
131
  "{Concurrent::Edge::#{$1} #{$1}}"
@@ -163,9 +155,9 @@ begin
163
155
  desc "* of #{name} into subdir #{name}"
164
156
  YARD::Rake::YardocTask.new(name) do |yard|
165
157
  yard.options.push(
166
- '--output-dir', output_dir,
167
- '--main', 'tmp/README.md',
168
- *common_yard_options)
158
+ '--output-dir', output_dir,
159
+ '--main', 'tmp/README.md',
160
+ *common_yard_options)
169
161
  yard.files = ['./lib/concurrent-ruby/**/*.rb',
170
162
  './lib/concurrent-ruby-edge/**/*.rb',
171
163
  './ext/concurrent_ruby_ext/**/*.c',
@@ -187,9 +179,9 @@ begin
187
179
  desc "* signpost for versions"
188
180
  YARD::Rake::YardocTask.new(:signpost) do |yard|
189
181
  yard.options.push(
190
- '--output-dir', 'docs',
191
- '--main', 'docs-source/signpost.md',
192
- *common_yard_options)
182
+ '--output-dir', 'docs',
183
+ '--main', 'docs-source/signpost.md',
184
+ *common_yard_options)
193
185
  yard.files = ['no-lib']
194
186
  end
195
187
 
@@ -204,7 +196,7 @@ begin
204
196
  sh 'diff -r docs/ docs-copy/' do |ok, res|
205
197
  unless ok
206
198
  begin
207
- STDOUT.puts 'Command failed. Continue? (y/n)'
199
+ STDOUT.puts "yard:#{name} is not properly generated and committed.", "Continue? (y/n)"
208
200
  input = STDIN.gets.strip.downcase
209
201
  end until %w(y n).include?(input)
210
202
  exit 1 if input == 'n'
@@ -232,15 +224,13 @@ task :release => ['release:checks', 'release:build', 'release:test', 'release:pu
232
224
  namespace :release do
233
225
  # Depends on environment of @pitr-ch
234
226
 
235
- mri_version = '2.6.5'
236
- jruby_version = 'jruby-9.2.9.0'
237
-
238
227
  task :checks => "yard:#{current_yard_version_name}:uptodate" do
239
228
  Dir.chdir(__dir__) do
240
229
  sh 'test -z "$(git status --porcelain)"' do |ok, res|
241
230
  unless ok
242
231
  begin
243
- STDOUT.puts 'Command failed. Continue? (y/n)'
232
+ status = `git status --porcelain`
233
+ STDOUT.puts 'There are local changes that you might want to commit.', status, 'Continue? (y/n)'
244
234
  input = STDIN.gets.strip.downcase
245
235
  end until %w(y n).include?(input)
246
236
  exit 1 if input == 'n'
@@ -248,10 +238,10 @@ namespace :release do
248
238
  end
249
239
  sh 'git fetch'
250
240
  sh 'test $(git show-ref --verify --hash refs/heads/master) = ' +
251
- '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
241
+ '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
252
242
  unless ok
253
243
  begin
254
- STDOUT.puts 'Command failed. Continue? (y/n)'
244
+ STDOUT.puts 'Local master branch is not pushed to origin.', 'Continue? (y/n)'
255
245
  input = STDIN.gets.strip.downcase
256
246
  end until %w(y n).include?(input)
257
247
  exit 1 if input == 'n'
@@ -268,6 +258,12 @@ namespace :release do
268
258
  Dir.chdir(__dir__) do
269
259
  old = ENV['RBENV_VERSION']
270
260
 
261
+ mri_version = `ruby -e 'puts RUBY_VERSION'`.chomp
262
+ jruby_version = File.basename(ENV['CONCURRENT_JRUBY_HOME'])
263
+
264
+ puts "Using following version:"
265
+ pp mri_version: mri_version, jruby_version: jruby_version
266
+
271
267
  ENV['RBENV_VERSION'] = mri_version
272
268
  sh 'rbenv version'
273
269
  sh 'bundle exec rake spec:installed'
@@ -290,19 +286,19 @@ namespace :release do
290
286
 
291
287
  task :ask do
292
288
  begin
293
- STDOUT.puts 'Do you want to publish anything? (y/n)'
289
+ STDOUT.puts 'Do you want to publish anything now? (y/n)'
294
290
  input = STDIN.gets.strip.downcase
295
291
  end until %w(y n).include?(input)
296
292
  exit 1 if input == 'n'
297
293
  begin
298
- STDOUT.puts 'Do you want to publish edge? (y/n)'
294
+ STDOUT.puts 'It will publish `concurrent-ruby`. Do you want to publish `concurrent-ruby-edge`? (y/n)'
299
295
  input = STDIN.gets.strip.downcase
300
296
  end until %w(y n).include?(input)
301
297
  publish_edge = input == 'y'
302
298
  end
303
299
 
304
300
  desc '** tag HEAD with current version and push to github'
305
- task :tag do
301
+ task :tag => :ask do
306
302
  Dir.chdir(__dir__) do
307
303
  sh "git tag v#{Concurrent::VERSION}"
308
304
  sh "git push origin v#{Concurrent::VERSION}"
@@ -312,7 +308,7 @@ namespace :release do
312
308
  end
313
309
 
314
310
  desc '** push all *.gem files to rubygems'
315
- task :rubygems do
311
+ task :rubygems => :ask do
316
312
  Dir.chdir(__dir__) do
317
313
  sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem"
318
314
  sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge
@@ -324,9 +320,10 @@ namespace :release do
324
320
 
325
321
  desc '** print post release steps'
326
322
  task :post_steps do
327
- puts 'Manually: create a release on GitHub with relevant changelog part'
328
- puts 'Manually: send email same as release with relevant changelog part'
329
- puts 'Manually: tweet'
323
+ # TODO: (petr 05-Jun-2021) automate and renew the process
324
+ # puts 'Manually: create a release on GitHub with relevant changelog part'
325
+ # puts 'Manually: send email same as release with relevant changelog part'
326
+ # puts 'Manually: tweet'
330
327
  end
331
328
  end
332
329
  end
@@ -10,6 +10,7 @@ import org.jruby.RubyNumeric;
10
10
  import org.jruby.RubyObject;
11
11
  import org.jruby.anno.JRubyClass;
12
12
  import org.jruby.anno.JRubyMethod;
13
+ import org.jruby.runtime.Block;
13
14
  import org.jruby.runtime.ObjectAllocator;
14
15
  import org.jruby.runtime.ThreadContext;
15
16
  import org.jruby.runtime.builtin.IRubyObject;
@@ -45,9 +46,13 @@ public class JavaSemaphoreLibrary {
45
46
  }
46
47
 
47
48
  @JRubyMethod
48
- public IRubyObject acquire(ThreadContext context, IRubyObject value) throws InterruptedException {
49
- this.semaphore.acquire(rubyFixnumToPositiveInt(value, "permits"));
50
- return context.nil;
49
+ public IRubyObject acquire(ThreadContext context, final Block block) throws InterruptedException {
50
+ return this.acquire(context, 1, block);
51
+ }
52
+
53
+ @JRubyMethod
54
+ public IRubyObject acquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException {
55
+ return this.acquire(context, rubyFixnumToPositiveInt(permits, "permits"), block);
51
56
  }
52
57
 
53
58
  @JRubyMethod(name = "available_permits")
@@ -60,30 +65,32 @@ public class JavaSemaphoreLibrary {
60
65
  return getRuntime().newFixnum(this.semaphore.drainPermits());
61
66
  }
62
67
 
63
- @JRubyMethod
64
- public IRubyObject acquire(ThreadContext context) throws InterruptedException {
65
- this.semaphore.acquire(1);
66
- return context.nil;
67
- }
68
-
69
68
  @JRubyMethod(name = "try_acquire")
70
- public IRubyObject tryAcquire(ThreadContext context) throws InterruptedException {
71
- return getRuntime().newBoolean(semaphore.tryAcquire(1));
69
+ public IRubyObject tryAcquire(ThreadContext context, final Block block) throws InterruptedException {
70
+ int permitsInt = 1;
71
+ boolean acquired = semaphore.tryAcquire(permitsInt);
72
+
73
+ return triedAcquire(context, permitsInt, acquired, block);
72
74
  }
73
75
 
74
76
  @JRubyMethod(name = "try_acquire")
75
- public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits) throws InterruptedException {
76
- return getRuntime().newBoolean(semaphore.tryAcquire(rubyFixnumToPositiveInt(permits, "permits")));
77
+ public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException {
78
+ int permitsInt = rubyFixnumToPositiveInt(permits, "permits");
79
+ boolean acquired = semaphore.tryAcquire(permitsInt);
80
+
81
+ return triedAcquire(context, permitsInt, acquired, block);
77
82
  }
78
83
 
79
84
  @JRubyMethod(name = "try_acquire")
80
- public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout) throws InterruptedException {
81
- return getRuntime().newBoolean(
82
- semaphore.tryAcquire(
83
- rubyFixnumToPositiveInt(permits, "permits"),
84
- rubyNumericToLong(timeout, "timeout"),
85
- java.util.concurrent.TimeUnit.SECONDS)
86
- );
85
+ public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout, final Block block) throws InterruptedException {
86
+ int permitsInt = rubyFixnumToPositiveInt(permits, "permits");
87
+ boolean acquired = semaphore.tryAcquire(
88
+ permitsInt,
89
+ rubyNumericToLong(timeout, "timeout"),
90
+ java.util.concurrent.TimeUnit.SECONDS
91
+ );
92
+
93
+ return triedAcquire(context, permitsInt, acquired, block);
87
94
  }
88
95
 
89
96
  @JRubyMethod
@@ -93,8 +100,8 @@ public class JavaSemaphoreLibrary {
93
100
  }
94
101
 
95
102
  @JRubyMethod
96
- public IRubyObject release(ThreadContext context, IRubyObject value) {
97
- this.semaphore.release(rubyFixnumToPositiveInt(value, "permits"));
103
+ public IRubyObject release(ThreadContext context, IRubyObject permits) {
104
+ this.semaphore.release(rubyFixnumToPositiveInt(permits, "permits"));
98
105
  return getRuntime().newBoolean(true);
99
106
  }
100
107
 
@@ -104,6 +111,29 @@ public class JavaSemaphoreLibrary {
104
111
  return context.nil;
105
112
  }
106
113
 
114
+ private IRubyObject acquire(ThreadContext context, int permits, final Block block) throws InterruptedException {
115
+ this.semaphore.acquire(permits);
116
+
117
+ if (!block.isGiven()) return context.nil;
118
+
119
+ try {
120
+ return block.yieldSpecific(context);
121
+ } finally {
122
+ this.semaphore.release(permits);
123
+ }
124
+ }
125
+
126
+ private IRubyObject triedAcquire(ThreadContext context, int permits, boolean acquired, final Block block) {
127
+ if (!block.isGiven()) return getRuntime().newBoolean(acquired);
128
+ if (!acquired) return context.nil;
129
+
130
+ try {
131
+ return block.yieldSpecific(context);
132
+ } finally {
133
+ this.semaphore.release(permits);
134
+ }
135
+ }
136
+
107
137
  private int rubyFixnumInt(IRubyObject value, String paramName) {
108
138
  if (value instanceof RubyFixnum) {
109
139
  RubyFixnum fixNum = (RubyFixnum) value;
@@ -272,6 +272,7 @@ module Concurrent
272
272
  obj.send(:init_synchronization)
273
273
  obj
274
274
  end
275
+ ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
275
276
  end
276
277
  private_constant :ClassMethods
277
278
 
@@ -170,6 +170,7 @@ module Concurrent
170
170
  alias_method :compare_and_swap, :compare_and_set
171
171
  alias_method :swap, :get_and_set
172
172
  end
173
+ TruffleRubyAtomicReference
173
174
  when Concurrent.on_rbx?
174
175
  # @note Extends `Rubinius::AtomicReference` version adding aliases
175
176
  # and numeric logic.
@@ -19,7 +19,7 @@ module Concurrent
19
19
  # t1 = Thread.new do
20
20
  # puts "t1 is waiting"
21
21
  # event.wait(1)
22
- # puts "event ocurred"
22
+ # puts "event occurred"
23
23
  # end
24
24
  #
25
25
  # t2 = Thread.new do
@@ -30,8 +30,8 @@ module Concurrent
30
30
  # [t1, t2].each(&:join)
31
31
  #
32
32
  # # prints:
33
- # # t2 calling set
34
33
  # # t1 is waiting
34
+ # # t2 calling set
35
35
  # # event occurred
36
36
  class Event < Synchronization::LockableObject
37
37
 
@@ -23,7 +23,14 @@ module Concurrent
23
23
 
24
24
  synchronize do
25
25
  try_acquire_timed(permits, nil)
26
- nil
26
+ end
27
+
28
+ return unless block_given?
29
+
30
+ begin
31
+ yield
32
+ ensure
33
+ release(permits)
27
34
  end
28
35
  end
29
36
 
@@ -48,13 +55,22 @@ module Concurrent
48
55
  Utility::NativeInteger.ensure_integer_and_bounds permits
49
56
  Utility::NativeInteger.ensure_positive permits
50
57
 
51
- synchronize do
58
+ acquired = synchronize do
52
59
  if timeout.nil?
53
60
  try_acquire_now(permits)
54
61
  else
55
62
  try_acquire_timed(permits, timeout)
56
63
  end
57
64
  end
65
+
66
+ return acquired unless block_given?
67
+ return unless acquired
68
+
69
+ begin
70
+ yield
71
+ ensure
72
+ release(permits)
73
+ end
58
74
  end
59
75
 
60
76
  # @!macro semaphore_method_release
@@ -267,12 +267,10 @@ module Concurrent
267
267
  # running right now, AND no writers who came before us still waiting to
268
268
  # acquire the lock
269
269
  # Additionally, if any read locks have been taken, we must hold all of them
270
- if c == held
271
- # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
272
- if @Counter.compare_and_set(c, c+RUNNING_WRITER)
273
- @HeldCount.value = held + WRITE_LOCK_HELD
274
- return true
275
- end
270
+ if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
271
+ # If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
272
+ @HeldCount.value = held + WRITE_LOCK_HELD
273
+ return true
276
274
  elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
277
275
  while true
278
276
  # Now we have successfully incremented, so no more readers will be able to increment
@@ -97,7 +97,10 @@ module Concurrent
97
97
  # The cost of GC'ing a TLV is linear in the number of threads using TLVs
98
98
  # But that is natural! More threads means more storage is used per TLV
99
99
  # So naturally more CPU time is required to free more storage
100
- THREAD_LOCAL_ARRAYS.each_value { |array| array[index] = nil }
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 }
101
104
  # free index has to be published after the arrays are cleared
102
105
  FREE.push(index)
103
106
  end
@@ -16,14 +16,16 @@ module Concurrent
16
16
  # @!macro semaphore_method_acquire
17
17
  #
18
18
  # Acquires the given number of permits from this semaphore,
19
- # blocking until all are available.
19
+ # blocking until all are available. If a block is given,
20
+ # yields to it and releases the permits afterwards.
20
21
  #
21
22
  # @param [Fixnum] permits Number of permits to acquire
22
23
  #
23
24
  # @raise [ArgumentError] if `permits` is not an integer or is less than
24
25
  # one
25
26
  #
26
- # @return [nil]
27
+ # @return [nil, BasicObject] Without a block, `nil` is returned. If a block
28
+ # is given, its return value is returned.
27
29
 
28
30
  # @!macro semaphore_method_available_permits
29
31
  #
@@ -41,7 +43,9 @@ module Concurrent
41
43
  #
42
44
  # Acquires the given number of permits from this semaphore,
43
45
  # only if all are available at the time of invocation or within
44
- # `timeout` interval
46
+ # `timeout` interval. If a block is given, yields to it if the permits
47
+ # were successfully acquired, and releases them afterward, returning the
48
+ # block's return value.
45
49
  #
46
50
  # @param [Fixnum] permits the number of permits to acquire
47
51
  #
@@ -51,8 +55,10 @@ module Concurrent
51
55
  # @raise [ArgumentError] if `permits` is not an integer or is less than
52
56
  # one
53
57
  #
54
- # @return [Boolean] `false` if no permits are available, `true` when
55
- # acquired a permit
58
+ # @return [true, false, nil, BasicObject] `false` if no permits are
59
+ # available, `true` when acquired a permit. If a block is given, the
60
+ # block's return value is returned if the permits were acquired; if not,
61
+ # `nil` is returned.
56
62
 
57
63
  # @!macro semaphore_method_release
58
64
  #
@@ -106,6 +112,8 @@ module Concurrent
106
112
  # releasing a blocking acquirer.
107
113
  # However, no actual permit objects are used; the Semaphore just keeps a
108
114
  # count of the number available and acts accordingly.
115
+ # Alternatively, permits may be acquired within a block, and automatically
116
+ # released after the block finishes executing.
109
117
  #
110
118
  # @!macro semaphore_public_api
111
119
  # @example
@@ -140,6 +148,19 @@ module Concurrent
140
148
  # # Thread 4 releasing semaphore
141
149
  # # Thread 1 acquired semaphore
142
150
  #
151
+ # @example
152
+ # semaphore = Concurrent::Semaphore.new(1)
153
+ #
154
+ # puts semaphore.available_permits
155
+ # semaphore.acquire do
156
+ # puts semaphore.available_permits
157
+ # end
158
+ # puts semaphore.available_permits
159
+ #
160
+ # # prints:
161
+ # # 1
162
+ # # 0
163
+ # # 1
143
164
  class Semaphore < SemaphoreImplementation
144
165
  end
145
166
  end
@@ -0,0 +1,14 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ module Collection
5
+
6
+ # @!visibility private
7
+ class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
8
+ def initialize(options = nil)
9
+ options ||= {}
10
+ super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
11
+ end
12
+ end
13
+ end
14
+ end