concurrent-ruby 1.1.8 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile +2 -7
  4. data/README.md +40 -20
  5. data/Rakefile +26 -31
  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/semaphore.rb +26 -5
  14. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  15. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  16. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  17. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +16 -13
  18. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  19. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +1 -1
  20. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  21. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  22. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  23. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +5 -5
  24. data/lib/concurrent-ruby/concurrent/map.rb +13 -4
  25. data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
  26. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +29 -16
  27. data/lib/concurrent-ruby/concurrent/set.rb +14 -6
  28. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +1 -3
  29. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
  30. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
  31. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
  32. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  33. data/lib/concurrent-ruby/concurrent/tvar.rb +20 -60
  34. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +67 -35
  35. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +2 -35
  36. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  37. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  38. metadata +8 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 207cadfd4ec0e25f6ba9881ef8f32ea86c5772074d4aff5e2ab5d5bdf4613e09
4
- data.tar.gz: b41c0dd080a6d2fbf1d2c97bf2e9a8db0e99849ba875d32ade39384838e20e23
3
+ metadata.gz: e2508d8671dc3f93a6d41204a69a2444669688ac1e9e7790104162ac5180579e
4
+ data.tar.gz: 5eba3636194c783d376ed010bd3d6ec8796fe409870e07a84c6b0b0ea2704b41
5
5
  SHA512:
6
- metadata.gz: 4edf0b84df6aa42a69c6ff231c730d12a64df6819ebea508599b469d88951c47ce4b091d3f3fc41afcb9196986091efe57d156161510f03cfb8ee0ebaf66ff0c
7
- data.tar.gz: a18c3ef751cadd220d424779e96d96ead76362ad4e7bfcfb2341d9ba4baed6f0b8427c452e31868e512ab2b3a8226fe350041e0e5b87cd28e10d9ce1b5334c72
6
+ metadata.gz: 4c1cbc5311e939aecda5e291bb579a690807de5240bb2fb30600a9d1d9de8c353558de7d6e3e0dff871fcca364bd7caa76b304428e0fdaba323cbe04be300056
7
+ data.tar.gz: 863635cad877062864813b9ba72685b3afdadabf258a62b36f8d7093a5be9c7115d64aade01c9a98b9e68ef86dff2698e5e0cef7766b9c4dfc740e0e76eccf0c
data/CHANGELOG.md CHANGED
@@ -1,7 +1,33 @@
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
+
3
27
  ## Release v1.1.8 (20 January 2021)
4
28
 
29
+ concurrent-ruby:
30
+
5
31
  * (#885) Fix race condition in TVar for stale reads
6
32
  * (#884) RubyThreadLocalVar: Do not iterate over hash which might conflict with new pair addition
7
33
 
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|
@@ -77,8 +68,7 @@ begin
77
68
  options = %w[ --color
78
69
  --backtrace
79
70
  --order defined
80
- --format documentation
81
- --tag ~notravis ]
71
+ --format documentation ]
82
72
  t.rspec_opts = [*options].join(' ')
83
73
  end
84
74
 
@@ -135,7 +125,7 @@ begin
135
125
  task :update_readme do
136
126
  Dir.chdir __dir__ do
137
127
  content = File.read(File.join('README.md')).
138
- gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
128
+ gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
139
129
  case $1
140
130
  when 'LockFreeLinkedSet'
141
131
  "{Concurrent::Edge::#{$1} #{$1}}"
@@ -165,9 +155,9 @@ begin
165
155
  desc "* of #{name} into subdir #{name}"
166
156
  YARD::Rake::YardocTask.new(name) do |yard|
167
157
  yard.options.push(
168
- '--output-dir', output_dir,
169
- '--main', 'tmp/README.md',
170
- *common_yard_options)
158
+ '--output-dir', output_dir,
159
+ '--main', 'tmp/README.md',
160
+ *common_yard_options)
171
161
  yard.files = ['./lib/concurrent-ruby/**/*.rb',
172
162
  './lib/concurrent-ruby-edge/**/*.rb',
173
163
  './ext/concurrent_ruby_ext/**/*.c',
@@ -189,9 +179,9 @@ begin
189
179
  desc "* signpost for versions"
190
180
  YARD::Rake::YardocTask.new(:signpost) do |yard|
191
181
  yard.options.push(
192
- '--output-dir', 'docs',
193
- '--main', 'docs-source/signpost.md',
194
- *common_yard_options)
182
+ '--output-dir', 'docs',
183
+ '--main', 'docs-source/signpost.md',
184
+ *common_yard_options)
195
185
  yard.files = ['no-lib']
196
186
  end
197
187
 
@@ -206,7 +196,7 @@ begin
206
196
  sh 'diff -r docs/ docs-copy/' do |ok, res|
207
197
  unless ok
208
198
  begin
209
- STDOUT.puts 'Command failed. Continue? (y/n)'
199
+ STDOUT.puts "yard:#{name} is not properly generated and committed.", "Continue? (y/n)"
210
200
  input = STDIN.gets.strip.downcase
211
201
  end until %w(y n).include?(input)
212
202
  exit 1 if input == 'n'
@@ -234,15 +224,13 @@ task :release => ['release:checks', 'release:build', 'release:test', 'release:pu
234
224
  namespace :release do
235
225
  # Depends on environment of @pitr-ch
236
226
 
237
- mri_version = '2.6.5'
238
- jruby_version = 'jruby-9.2.9.0'
239
-
240
227
  task :checks => "yard:#{current_yard_version_name}:uptodate" do
241
228
  Dir.chdir(__dir__) do
242
229
  sh 'test -z "$(git status --porcelain)"' do |ok, res|
243
230
  unless ok
244
231
  begin
245
- 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)'
246
234
  input = STDIN.gets.strip.downcase
247
235
  end until %w(y n).include?(input)
248
236
  exit 1 if input == 'n'
@@ -250,10 +238,10 @@ namespace :release do
250
238
  end
251
239
  sh 'git fetch'
252
240
  sh 'test $(git show-ref --verify --hash refs/heads/master) = ' +
253
- '$(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|
254
242
  unless ok
255
243
  begin
256
- STDOUT.puts 'Command failed. Continue? (y/n)'
244
+ STDOUT.puts 'Local master branch is not pushed to origin.', 'Continue? (y/n)'
257
245
  input = STDIN.gets.strip.downcase
258
246
  end until %w(y n).include?(input)
259
247
  exit 1 if input == 'n'
@@ -270,6 +258,12 @@ namespace :release do
270
258
  Dir.chdir(__dir__) do
271
259
  old = ENV['RBENV_VERSION']
272
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
+
273
267
  ENV['RBENV_VERSION'] = mri_version
274
268
  sh 'rbenv version'
275
269
  sh 'bundle exec rake spec:installed'
@@ -292,12 +286,12 @@ namespace :release do
292
286
 
293
287
  task :ask do
294
288
  begin
295
- STDOUT.puts 'Do you want to publish anything? (y/n)'
289
+ STDOUT.puts 'Do you want to publish anything now? (y/n)'
296
290
  input = STDIN.gets.strip.downcase
297
291
  end until %w(y n).include?(input)
298
292
  exit 1 if input == 'n'
299
293
  begin
300
- 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)'
301
295
  input = STDIN.gets.strip.downcase
302
296
  end until %w(y n).include?(input)
303
297
  publish_edge = input == 'y'
@@ -326,9 +320,10 @@ namespace :release do
326
320
 
327
321
  desc '** print post release steps'
328
322
  task :post_steps do
329
- puts 'Manually: create a release on GitHub with relevant changelog part'
330
- puts 'Manually: send email same as release with relevant changelog part'
331
- 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'
332
327
  end
333
328
  end
334
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
@@ -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
@@ -30,7 +30,7 @@ module Concurrent
30
30
  if @queue[k] == item
31
31
  swap(k, @length)
32
32
  @length -= 1
33
- sink(k)
33
+ sink(k) || swim(k)
34
34
  @queue.pop
35
35
  else
36
36
  k += 1
@@ -126,12 +126,17 @@ module Concurrent
126
126
  #
127
127
  # @!visibility private
128
128
  def sink(k)
129
+ success = false
130
+
129
131
  while (j = (2 * k)) <= @length do
130
132
  j += 1 if j < @length && ! ordered?(j, j+1)
131
133
  break if ordered?(k, j)
132
134
  swap(k, j)
135
+ success = true
133
136
  k = j
134
137
  end
138
+
139
+ success
135
140
  end
136
141
 
137
142
  # Percolate up to maintain heap invariant.
@@ -140,10 +145,15 @@ module Concurrent
140
145
  #
141
146
  # @!visibility private
142
147
  def swim(k)
148
+ success = false
149
+
143
150
  while k > 1 && ! ordered?(k/2, k) do
144
151
  swap(k, k/2)
145
152
  k = k/2
153
+ success = true
146
154
  end
155
+
156
+ success
147
157
  end
148
158
  end
149
159
  end