Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-concurrent-ruby for openSUSE:Factory checked in at 2022-05-17 17:23:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-concurrent-ruby (Old) and /work/SRC/openSUSE:Factory/.rubygem-concurrent-ruby.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-concurrent-ruby" Tue May 17 17:23:32 2022 rev:14 rq:977370 version:1.1.10 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-concurrent-ruby/rubygem-concurrent-ruby.changes 2021-07-04 22:10:25.989372797 +0200 +++ /work/SRC/openSUSE:Factory/.rubygem-concurrent-ruby.new.1538/rubygem-concurrent-ruby.changes 2022-05-17 17:23:32.559122383 +0200 @@ -1,0 +2,15 @@ +Sun May 15 15:28:32 UTC 2022 - Manuel Schnitzer <mschnit...@suse.com> + +- updated to version 1.1.10 + + * (#951) Set the Ruby compatibility version at 2.2 + * (#939, #933) The `caller_runs` fallback policy no longer blocks reads from the job queue by worker threads + * (#938, #761, #652) You can now explicitly `prune_pool` a thread pool (Sylvain Joyeux) + * (#937, #757, #670) We switched the Yahoo stock API for demos to Alpha Vantage (Gustavo Caso) + * (#932, #931) We changed how `SafeTaskExecutor` handles local jump errors (Aaron Jensen) + * (#927) You can use keyword arguments in your initialize when using `Async` (Matt Larraz) + * (#926, #639) We removed timeout from `TimerTask` because it wasn't sound, and now it's a no-op with a warning (Jacob Atzen) + * (#919) If you double-lock a re-entrant read-write lock, we promote to locked for writing (zp yuan) + * (#915) `monotonic_time` now accepts an optional unit parameter, as Ruby's `clock_gettime` (Jean Boussier) + +------------------------------------------------------------------- Old: ---- concurrent-ruby-1.1.9.gem New: ---- concurrent-ruby-1.1.10.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-concurrent-ruby.spec ++++++ --- /var/tmp/diff_new_pack.SrXacb/_old 2022-05-17 17:23:33.135122908 +0200 +++ /var/tmp/diff_new_pack.SrXacb/_new 2022-05-17 17:23:33.139122911 +0200 @@ -1,7 +1,7 @@ # # spec file for package rubygem-concurrent-ruby # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,12 +24,12 @@ # Name: rubygem-concurrent-ruby -Version: 1.1.9 +Version: 1.1.10 Release: 0 %define mod_name concurrent-ruby %define mod_full_name %{mod_name}-%{version} BuildRoot: %{_tmppath}/%{name}-%{version}-build -BuildRequires: %{ruby >= 1.9.3} +BuildRequires: %{ruby >= 2.2} BuildRequires: %{rubygem gem2rpm} BuildRequires: ruby-macros >= 5 URL: http://www.concurrent-ruby.com ++++++ concurrent-ruby-1.1.9.gem -> concurrent-ruby-1.1.10.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md --- old/CHANGELOG.md 2021-06-05 14:47:58.000000000 +0200 +++ new/CHANGELOG.md 2022-03-22 01:03:48.000000000 +0100 @@ -1,5 +1,19 @@ ## Current +## Release v1.1.10 + +concurrent-ruby: + +* (#951) Set the Ruby compatibility version at 2.2 +* (#939, #933) The `caller_runs` fallback policy no longer blocks reads from the job queue by worker threads +* (#938, #761, #652) You can now explicitly `prune_pool` a thread pool (Sylvain Joyeux) +* (#937, #757, #670) We switched the Yahoo stock API for demos to Alpha Vantage (Gustavo Caso) +* (#932, #931) We changed how `SafeTaskExecutor` handles local jump errors (Aaron Jensen) +* (#927) You can use keyword arguments in your initialize when using `Async` (Matt Larraz) +* (#926, #639) We removed timeout from `TimerTask` because it wasn't sound, and now it's a no-op with a warning (Jacob Atzen) +* (#919) If you double-lock a re-entrant read-write lock, we promote to locked for writing (zp yuan) +* (#915) `monotonic_time` now accepts an optional unit parameter, as Ruby's `clock_gettime` (Jean Boussier) + ## Release v1.1.9 (5 Jun 2021) concurrent-ruby: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Gemfile new/Gemfile --- old/Gemfile 2021-06-05 14:47:58.000000000 +0200 +++ new/Gemfile 2022-03-22 01:03:48.000000000 +0100 @@ -12,7 +12,7 @@ gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri) group :development do - gem 'rake', (Concurrent.ruby_version :<, 2, 2, 0) ? '~> 12.0' : '~> 13.0' + gem 'rake', '~> 13.0' gem 'rake-compiler', '~> 1.0', '>= 1.0.7' gem 'rake-compiler-dock', '~> 1.0' gem 'pry', '~> 0.11', platforms: :mri @@ -26,7 +26,7 @@ group :testing do gem 'rspec', '~> 3.7' - gem 'timecop', '~> 0.7.4' + gem 'timecop', '~> 0.9' gem 'sigdump', require: false end @@ -35,8 +35,3 @@ gem 'simplecov', '~> 0.16.0', require: false gem 'coveralls', '~> 0.8.2', require: false end - -group :benchmarks, optional: true do - gem 'benchmark-ips', '~> 2.7' - gem 'bench9000' -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2021-06-05 14:47:58.000000000 +0200 +++ new/README.md 2022-03-22 01:03:48.000000000 +0100 @@ -1,8 +1,6 @@ # Concurrent Ruby [](http://badge.fury.io/rb/concurrent-ruby) -[](https://travis-ci.org/ruby-concurrency/concurrent-ruby) -[](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby) [](http://opensource.org/licenses/MIT) [-devs%20%26%20users-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby) @@ -39,6 +37,8 @@ appreciate your help. Would you like to contribute? Great! Have a look at [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. +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). [](https://www.codetriage.com/ruby-concurrency/concurrent-ruby) + ## Thread Safety *Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency @@ -259,15 +259,11 @@ ## Supported Ruby versions -* MRI 2.0 and above -* JRuby 9000 -* TruffleRuby are supported. -* Any Ruby interpreter that is compliant with Ruby 2.0 or newer. - -Actually we still support mri 1.9.3 and jruby 1.7.27 but we are looking at ways how to drop the support. -Java 8 is preferred for JRuby but every Java version on which JRuby 9000 runs is supported. +* MRI 2.2 and above +* Latest JRuby 9000 +* Latest TruffleRuby -The legacy support for Rubinius is kept but it is no longer maintained, if you would like to help +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 please respond to [#739](https://github.com/ruby-concurrency/concurrent-ruby/issues/739). ## Usage @@ -364,7 +360,7 @@ ### Publishing the Gem -* Update`version.rb` +* Update `version.rb` * Update the CHANGELOG * Update the Yard documentation - Add the new version to `docs-source/signpost.md`. Needs to be done only if there are visible changes in the @@ -378,22 +374,22 @@ ## Maintainers -* [Petr Chalupa](https://github.com/pitr-ch) ??? Lead maintainer, point-of-contact. -* [Chris Seaton](https://github.com/chrisseaton) ??? - If Petr is not available Chris can help or poke Petr to pay attention where it is needed. +* [Chris Seaton](https://github.com/chrisseaton) ??? Lead maintainer, point-of-contact. +* [Benoit Daloze](https://github.com/eregon) ??? If Chris is not available Benoit can help. ### Special Thanks to -* [Jerry D'Antonio](https://github.com/jdantonio) for creating the gem -* [Brian Durand](https://github.com/bdurand) for the `ref` gem -* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems -* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem +* [Jerry D'Antonio](https://github.com/jdantonio) for creating the gem +* [Brian Durand](https://github.com/bdurand) for the `ref` gem +* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems +* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem to the past maintainers -* [Michele Della Torre](https://github.com/mighe) -* [Pawe?? Obrok](https://github.com/obrok) -* [Lucas Allan](https://github.com/lucasallan) +* [Petr Chalupa](https://github.com/pitr-ch) +* [Michele Della Torre](https://github.com/mighe) +* [Pawe?? Obrok](https://github.com/obrok) +* [Lucas Allan](https://github.com/lucasallan) and to [Ruby Association](https://www.ruby.or.jp/en/) for sponsoring a project ["Enhancing Ruby???s concurrency tooling"](https://www.ruby.or.jp/en/news/20181106) in 2018. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Rakefile new/Rakefile --- old/Rakefile 2021-06-05 14:47:58.000000000 +0200 +++ new/Rakefile 2022-03-22 01:03:48.000000000 +0100 @@ -2,15 +2,6 @@ require_relative 'lib/concurrent-ruby-edge/concurrent/edge/version' require_relative 'lib/concurrent-ruby/concurrent/utility/engine' -if Concurrent.ruby_version :<, 2, 0, 0 - # @!visibility private - module Kernel - def __dir__ - File.dirname __FILE__ - end - end -end - core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec') ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec') edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec') @@ -24,7 +15,7 @@ ext.lib_dir = 'lib/concurrent-ruby/concurrent' end -unless Concurrent.on_jruby? +unless Concurrent.on_jruby? || Concurrent.on_truffleruby? require 'rake/extensiontask' Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext| @@ -77,8 +68,7 @@ options = %w[ --color --backtrace --order defined - --format documentation - --tag ~notravis ] + --format documentation ] t.rspec_opts = [*options].join(' ') end Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java new/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java --- old/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java 2021-06-05 14:47:58.000000000 +0200 +++ new/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java 2022-03-22 01:03:48.000000000 +0100 @@ -10,6 +10,7 @@ import org.jruby.RubyObject; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; @@ -45,9 +46,13 @@ } @JRubyMethod - public IRubyObject acquire(ThreadContext context, IRubyObject value) throws InterruptedException { - this.semaphore.acquire(rubyFixnumToPositiveInt(value, "permits")); - return context.nil; + public IRubyObject acquire(ThreadContext context, final Block block) throws InterruptedException { + return this.acquire(context, 1, block); + } + + @JRubyMethod + public IRubyObject acquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException { + return this.acquire(context, rubyFixnumToPositiveInt(permits, "permits"), block); } @JRubyMethod(name = "available_permits") @@ -60,30 +65,32 @@ return getRuntime().newFixnum(this.semaphore.drainPermits()); } - @JRubyMethod - public IRubyObject acquire(ThreadContext context) throws InterruptedException { - this.semaphore.acquire(1); - return context.nil; - } - @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context) throws InterruptedException { - return getRuntime().newBoolean(semaphore.tryAcquire(1)); + public IRubyObject tryAcquire(ThreadContext context, final Block block) throws InterruptedException { + int permitsInt = 1; + boolean acquired = semaphore.tryAcquire(permitsInt); + + return triedAcquire(context, permitsInt, acquired, block); } @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits) throws InterruptedException { - return getRuntime().newBoolean(semaphore.tryAcquire(rubyFixnumToPositiveInt(permits, "permits"))); + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, final Block block) throws InterruptedException { + int permitsInt = rubyFixnumToPositiveInt(permits, "permits"); + boolean acquired = semaphore.tryAcquire(permitsInt); + + return triedAcquire(context, permitsInt, acquired, block); } @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout) throws InterruptedException { - return getRuntime().newBoolean( - semaphore.tryAcquire( - rubyFixnumToPositiveInt(permits, "permits"), - rubyNumericToLong(timeout, "timeout"), - java.util.concurrent.TimeUnit.SECONDS) - ); + public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout, final Block block) throws InterruptedException { + int permitsInt = rubyFixnumToPositiveInt(permits, "permits"); + boolean acquired = semaphore.tryAcquire( + permitsInt, + rubyNumericToLong(timeout, "timeout"), + java.util.concurrent.TimeUnit.SECONDS + ); + + return triedAcquire(context, permitsInt, acquired, block); } @JRubyMethod @@ -93,8 +100,8 @@ } @JRubyMethod - public IRubyObject release(ThreadContext context, IRubyObject value) { - this.semaphore.release(rubyFixnumToPositiveInt(value, "permits")); + public IRubyObject release(ThreadContext context, IRubyObject permits) { + this.semaphore.release(rubyFixnumToPositiveInt(permits, "permits")); return getRuntime().newBoolean(true); } @@ -104,6 +111,29 @@ return context.nil; } + private IRubyObject acquire(ThreadContext context, int permits, final Block block) throws InterruptedException { + this.semaphore.acquire(permits); + + if (!block.isGiven()) return context.nil; + + try { + return block.yieldSpecific(context); + } finally { + this.semaphore.release(permits); + } + } + + private IRubyObject triedAcquire(ThreadContext context, int permits, boolean acquired, final Block block) { + if (!block.isGiven()) return getRuntime().newBoolean(acquired); + if (!acquired) return context.nil; + + try { + return block.yieldSpecific(context); + } finally { + this.semaphore.release(permits); + } + } + private int rubyFixnumInt(IRubyObject value, String paramName) { if (value instanceof RubyFixnum) { RubyFixnum fixNum = (RubyFixnum) value; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/async.rb new/lib/concurrent-ruby/concurrent/async.rb --- old/lib/concurrent-ruby/concurrent/async.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/async.rb 2022-03-22 01:03:48.000000000 +0100 @@ -272,6 +272,7 @@ obj.send(:init_synchronization) obj end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) end private_constant :ClassMethods diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb new/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb --- old/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb 2022-03-22 01:03:48.000000000 +0100 @@ -170,6 +170,7 @@ alias_method :compare_and_swap, :compare_and_set alias_method :swap, :get_and_set end + TruffleRubyAtomicReference when Concurrent.on_rbx? # @note Extends `Rubinius::AtomicReference` version adding aliases # and numeric logic. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/atomic/event.rb new/lib/concurrent-ruby/concurrent/atomic/event.rb --- old/lib/concurrent-ruby/concurrent/atomic/event.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/atomic/event.rb 2022-03-22 01:03:48.000000000 +0100 @@ -19,7 +19,7 @@ # t1 = Thread.new do # puts "t1 is waiting" # event.wait(1) - # puts "event ocurred" + # puts "event occurred" # end # # t2 = Thread.new do @@ -30,8 +30,8 @@ # [t1, t2].each(&:join) # # # prints: - # # t2 calling set # # t1 is waiting + # # t2 calling set # # event occurred class Event < Synchronization::LockableObject diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb new/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb --- old/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb 2022-03-22 01:03:48.000000000 +0100 @@ -23,7 +23,14 @@ synchronize do try_acquire_timed(permits, nil) - nil + end + + return unless block_given? + + begin + yield + ensure + release(permits) end end @@ -48,13 +55,22 @@ Utility::NativeInteger.ensure_integer_and_bounds permits Utility::NativeInteger.ensure_positive permits - synchronize do + acquired = synchronize do if timeout.nil? try_acquire_now(permits) else try_acquire_timed(permits, timeout) end end + + return acquired unless block_given? + return unless acquired + + begin + yield + ensure + release(permits) + end end # @!macro semaphore_method_release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb new/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb --- old/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb 2022-03-22 01:03:48.000000000 +0100 @@ -267,12 +267,10 @@ # running right now, AND no writers who came before us still waiting to # acquire the lock # Additionally, if any read locks have been taken, we must hold all of them - if c == held - # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead - if @Counter.compare_and_set(c, c+RUNNING_WRITER) - @HeldCount.value = held + WRITE_LOCK_HELD - return true - end + if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER) + # If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead + @HeldCount.value = held + WRITE_LOCK_HELD + return true elsif @Counter.compare_and_set(c, c+WAITING_WRITER) while true # Now we have successfully incremented, so no more readers will be able to increment diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/atomic/semaphore.rb new/lib/concurrent-ruby/concurrent/atomic/semaphore.rb --- old/lib/concurrent-ruby/concurrent/atomic/semaphore.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/atomic/semaphore.rb 2022-03-22 01:03:48.000000000 +0100 @@ -16,14 +16,16 @@ # @!macro semaphore_method_acquire # # Acquires the given number of permits from this semaphore, - # blocking until all are available. + # blocking until all are available. If a block is given, + # yields to it and releases the permits afterwards. # # @param [Fixnum] permits Number of permits to acquire # # @raise [ArgumentError] if `permits` is not an integer or is less than # one # - # @return [nil] + # @return [nil, BasicObject] Without a block, `nil` is returned. If a block + # is given, its return value is returned. # @!macro semaphore_method_available_permits # @@ -41,7 +43,9 @@ # # Acquires the given number of permits from this semaphore, # only if all are available at the time of invocation or within - # `timeout` interval + # `timeout` interval. If a block is given, yields to it if the permits + # were successfully acquired, and releases them afterward, returning the + # block's return value. # # @param [Fixnum] permits the number of permits to acquire # @@ -51,8 +55,10 @@ # @raise [ArgumentError] if `permits` is not an integer or is less than # one # - # @return [Boolean] `false` if no permits are available, `true` when - # acquired a permit + # @return [true, false, nil, BasicObject] `false` if no permits are + # available, `true` when acquired a permit. If a block is given, the + # block's return value is returned if the permits were acquired; if not, + # `nil` is returned. # @!macro semaphore_method_release # @@ -106,6 +112,8 @@ # releasing a blocking acquirer. # However, no actual permit objects are used; the Semaphore just keeps a # count of the number available and acts accordingly. + # Alternatively, permits may be acquired within a block, and automatically + # released after the block finishes executing. # # @!macro semaphore_public_api # @example @@ -140,6 +148,19 @@ # # Thread 4 releasing semaphore # # Thread 1 acquired semaphore # + # @example + # semaphore = Concurrent::Semaphore.new(1) + # + # puts semaphore.available_permits + # semaphore.acquire do + # puts semaphore.available_permits + # end + # puts semaphore.available_permits + # + # # prints: + # # 1 + # # 0 + # # 1 class Semaphore < SemaphoreImplementation end end Binary files old/lib/concurrent-ruby/concurrent/concurrent_ruby.jar and new/lib/concurrent-ruby/concurrent/concurrent_ruby.jar differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb new/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb --- old/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb 2022-03-22 01:03:48.000000000 +0100 @@ -75,28 +75,31 @@ private - # Handler which executes the `fallback_policy` once the queue size - # reaches `max_queue`. + # Returns an action which executes the `fallback_policy` once the queue + # size reaches `max_queue`. The reason for the indirection of an action + # is so that the work can be deferred outside of synchronization. # # @param [Array] args the arguments to the task which is being handled. # # @!visibility private - def handle_fallback(*args) + def fallback_action(*args) case fallback_policy when :abort - raise RejectedExecutionError + lambda { raise RejectedExecutionError } when :discard - false + lambda { false } when :caller_runs - begin - yield(*args) - rescue => ex - # let it fail - log DEBUG, ex - end - true + lambda { + begin + yield(*args) + rescue => ex + # let it fail + log DEBUG, ex + end + true + } else - fail "Unknown fallback policy #{fallback_policy}" + lambda { fail "Unknown fallback policy #{fallback_policy}" } end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb new/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb --- old/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb 2022-03-22 01:03:48.000000000 +0100 @@ -71,9 +71,16 @@ # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting # new tasks. A value of -1 indicates that the queue may grow without bound. - - - + # @!macro thread_pool_executor_method_prune_pool + # Prune the thread pool of unneeded threads + # + # What is being pruned is controlled by the min_threads and idletime + # parameters passed at pool creation time + # + # This is a no-op on some pool implementation (e.g. the Java one). The Ruby + # pool will auto-prune each time a new job is posted. You will need to call + # this method explicitely in case your application post jobs in bursts (a + # lot of jobs and then nothing for long periods) # @!macro thread_pool_executor_public_api # @@ -111,6 +118,9 @@ # # @!method can_overflow? # @!macro executor_service_method_can_overflow_question + # + # @!method prune_pool + # @!macro thread_pool_executor_method_prune_pool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb new/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb --- old/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb 2022-03-22 01:03:48.000000000 +0100 @@ -20,7 +20,7 @@ def post(*args, &task) raise ArgumentError.new('no block given') unless block_given? - return handle_fallback(*args, &task) unless running? + return fallback_action(*args, &task).call unless running? @executor.submit Job.new(args, task) true rescue Java::JavaUtilConcurrent::RejectedExecutionException diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb new/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb --- old/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb 2022-03-22 01:03:48.000000000 +0100 @@ -93,6 +93,10 @@ super && !@executor.isTerminating end + # @!macro thread_pool_executor_method_prune_pool + def prune_pool + end + private def ns_initialize(opts) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb new/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb --- old/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb 2022-03-22 01:03:48.000000000 +0100 @@ -16,10 +16,16 @@ def post(*args, &task) raise ArgumentError.new('no block given') unless block_given? - synchronize do - # If the executor is shut down, reject this task - return handle_fallback(*args, &task) unless running? - ns_execute(*args, &task) + deferred_action = synchronize { + if running? + ns_execute(*args, &task) + else + fallback_action(*args, &task) + end + } + if deferred_action + deferred_action.call + else true end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb new/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb --- old/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb 2022-03-22 01:03:48.000000000 +0100 @@ -93,13 +93,8 @@ end # @!visibility private - def ready_worker(worker) - synchronize { ns_ready_worker worker } - end - - # @!visibility private - def worker_not_old_enough(worker) - synchronize { ns_worker_not_old_enough worker } + def ready_worker(worker, last_message) + synchronize { ns_ready_worker worker, last_message } end # @!visibility private @@ -112,6 +107,11 @@ synchronize { @completed_task_count += 1 } end + # @!macro thread_pool_executor_method_prune_pool + def prune_pool + synchronize { ns_prune_pool } + end + private # @!visibility private @@ -156,10 +156,11 @@ if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task) @scheduled_task_count += 1 else - handle_fallback(*args, &task) + return fallback_action(*args, &task) end ns_prune_pool if @next_gc_time < Concurrent.monotonic_time + nil end # @!visibility private @@ -192,7 +193,7 @@ # @!visibility private def ns_assign_worker(*args, &task) # keep growing if the pool is not at the minimum yet - worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker + worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker if worker worker << [task, args] true @@ -223,7 +224,7 @@ def ns_worker_died(worker) ns_remove_busy_worker worker replacement_worker = ns_add_busy_worker - ns_ready_worker replacement_worker, false if replacement_worker + ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker end # creates new worker which has to receive work to do after it's added @@ -242,29 +243,21 @@ # handle ready worker, giving it new job or assigning back to @ready # # @!visibility private - def ns_ready_worker(worker, success = true) + def ns_ready_worker(worker, last_message, success = true) task_and_args = @queue.shift if task_and_args worker << task_and_args else # stop workers when !running?, do not return them to @ready if running? - @ready.push(worker) + raise unless last_message + @ready.push([worker, last_message]) else worker.stop end end end - # returns back worker to @ready which was not idle for enough time - # - # @!visibility private - def ns_worker_not_old_enough(worker) - # let's put workers coming from idle_test back to the start (as the oldest worker) - @ready.unshift(worker) - true - end - # removes a worker which is not in not tracked in @ready # # @!visibility private @@ -278,10 +271,17 @@ # # @!visibility private def ns_prune_pool - return if @pool.size <= @min_length - - last_used = @ready.shift - last_used << :idle_test if last_used + now = Concurrent.monotonic_time + stopped_workers = 0 + while !@ready.empty? && (@pool.size - stopped_workers > @min_length) + worker, last_message = @ready.first + if now - last_message > self.idletime + stopped_workers += 1 + @ready.shift + worker << :stop + else break + end + end @next_gc_time = Concurrent.monotonic_time + @gc_interval end @@ -330,19 +330,10 @@ def create_worker(queue, pool, idletime) Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime| - last_message = Concurrent.monotonic_time catch(:stop) do loop do case message = my_queue.pop - when :idle_test - if (Concurrent.monotonic_time - last_message) > my_idletime - my_pool.remove_busy_worker(self) - throw :stop - else - my_pool.worker_not_old_enough(self) - end - when :stop my_pool.remove_busy_worker(self) throw :stop @@ -350,9 +341,7 @@ else task, args = message run_task my_pool, task, args - last_message = Concurrent.monotonic_time - - my_pool.ready_worker(self) + my_pool.ready_worker(self, Concurrent.monotonic_time) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb new/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb --- old/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb 2022-03-22 01:03:48.000000000 +0100 @@ -16,10 +16,10 @@ # @return [Array] def execute(*args) - synchronize do - success = false - value = reason = nil + success = true + value = reason = nil + synchronize do begin value = @task.call(*args) success = true @@ -27,9 +27,9 @@ reason = ex success = false end - - [success, value, reason] end + + [success, value, reason] end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/map.rb new/lib/concurrent-ruby/concurrent/map.rb --- old/lib/concurrent-ruby/concurrent/map.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/map.rb 2022-03-22 01:03:48.000000000 +0100 @@ -281,7 +281,6 @@ each_pair { |k, v| return k if v == value } nil end unless method_defined?(:key) - alias_method :index, :key if RUBY_VERSION < '1.9' # Is map empty? # @return [true, false] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/scheduled_task.rb new/lib/concurrent-ruby/concurrent/scheduled_task.rb --- old/lib/concurrent-ruby/concurrent/scheduled_task.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/scheduled_task.rb 2022-03-22 01:03:48.000000000 +0100 @@ -58,29 +58,42 @@ # @example Basic usage # # require 'concurrent' - # require 'thread' # for Queue - # require 'open-uri' # for open(uri) + # require 'csv' + # require 'open-uri' # # class Ticker - # def get_year_end_closing(symbol, year) - # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m" - # data = open(uri) {|f| f.collect{|line| line.strip } } - # data[1].split(',')[4].to_f - # end + # def get_year_end_closing(symbol, year, api_key) + # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv" + # data = [] + # csv = URI.parse(uri).read + # if csv.include?('call frequency') + # return :rate_limit_exceeded + # end + # CSV.parse(csv, headers: true) do |row| + # data << row['close'].to_f if row['timestamp'].include?(year.to_s) + # end + # year_end = data.first + # year_end + # rescue => e + # p e + # end # end # + # api_key = ENV['ALPHAVANTAGE_KEY'] + # abort(error_message) unless api_key + # # # Future - # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) } + # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) } # price.state #=> :pending - # sleep(1) # do other stuff - # price.value #=> 63.65 - # price.state #=> :fulfilled + # price.pending? #=> true + # price.value(0) #=> nil (does not block) # - # # ScheduledTask - # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) } - # task.state #=> :pending - # sleep(3) # do other stuff - # task.value #=> 25.96 + # sleep(1) # do other stuff + # + # price.value #=> 63.65 (after blocking if necessary) + # price.state #=> :fulfilled + # price.fulfilled? #=> true + # price.value #=> 63.65 # # @example Successful task execution # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb new/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb --- old/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb 2022-03-22 01:03:48.000000000 +0100 @@ -4,9 +4,7 @@ # @!visibility private # @!macro internal_implementation_note LockableObjectImplementation = case - when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3) - MonitorLockableObject - when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3) + when Concurrent.on_cruby? MutexLockableObject when Concurrent.on_jruby? JRubyLockableObject diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/timer_task.rb new/lib/concurrent-ruby/concurrent/timer_task.rb --- old/lib/concurrent-ruby/concurrent/timer_task.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/timer_task.rb 2022-03-22 01:03:48.000000000 +0100 @@ -25,9 +25,7 @@ # Should the task experience an unrecoverable crash only the task thread will # crash. This makes the `TimerTask` very fault tolerant. Additionally, the # `TimerTask` thread can respond to the success or failure of the task, - # performing logging or ancillary operations. `TimerTask` can also be - # configured with a timeout value allowing it to kill a task that runs too - # long. + # performing logging or ancillary operations. # # One other advantage of `TimerTask` is that it forces the business logic to # be completely decoupled from the concurrency logic. The business logic can @@ -48,9 +46,7 @@ # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html # Observable} module. On execution the `TimerTask` will notify the observers # with three arguments: time of execution, the result of the block (or nil on - # failure), and any raised exceptions (or nil on success). If the timeout - # interval is exceeded the observer will receive a `Concurrent::TimeoutError` - # object as the third argument. + # failure), and any raised exceptions (or nil on success). # # @!macro copy_options # @@ -59,20 +55,18 @@ # task.execute # # task.execution_interval #=> 60 (default) - # task.timeout_interval #=> 30 (default) # # # wait 60 seconds... # #=> 'Boom!' # # task.shutdown #=> true # - # @example Configuring `:execution_interval` and `:timeout_interval` - # task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do + # @example Configuring `:execution_interval` + # task = Concurrent::TimerTask.new(execution_interval: 5) do # puts 'Boom!' # end # # task.execution_interval #=> 5 - # task.timeout_interval #=> 5 # # @example Immediate execution with `:run_now` # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' } @@ -115,15 +109,13 @@ # def update(time, result, ex) # if result # print "(#{time}) Execution successfully returned #{result}\n" - # elsif ex.is_a?(Concurrent::TimeoutError) - # print "(#{time}) Execution timed out\n" # else # print "(#{time}) Execution failed with error #{ex}\n" # end # end # end # - # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 } + # task = Concurrent::TimerTask.new(execution_interval: 1){ 42 } # task.add_observer(TaskObserver.new) # task.execute # sleep 4 @@ -133,7 +125,7 @@ # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42 # task.shutdown # - # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ sleep } + # task = Concurrent::TimerTask.new(execution_interval: 1){ sleep } # task.add_observer(TaskObserver.new) # task.execute # @@ -169,8 +161,6 @@ # @param [Hash] opts the options defining task execution. # @option opts [Integer] :execution_interval number of seconds between # task executions (default: EXECUTION_INTERVAL) - # @option opts [Integer] :timeout_interval number of seconds a task can - # run before it is considered to have failed (default: TIMEOUT_INTERVAL) # @option opts [Boolean] :run_now Whether to run the task immediately # upon instantiation or to wait until the first # execution_interval # has passed (default: false) @@ -256,18 +246,14 @@ # @return [Fixnum] Number of seconds the task can run before it is # considered to have failed. def timeout_interval - synchronize { @timeout_interval } + warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly' end # @!attribute [rw] timeout_interval # @return [Fixnum] Number of seconds the task can run before it is # considered to have failed. def timeout_interval=(value) - if (value = value.to_f) <= 0.0 - raise ArgumentError.new('must be greater than zero') - else - synchronize { @timeout_interval = value } - end + warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly' end private :post, :<< @@ -278,7 +264,9 @@ set_deref_options(opts) self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL - self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL + if opts[:timeout] || opts[:timeout_interval] + warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly' + end @run_now = opts[:now] || opts[:run_now] @executor = Concurrent::SafeTaskExecutor.new(task) @running = Concurrent::AtomicBoolean.new(false) @@ -308,7 +296,6 @@ # @!visibility private def execute_task(completion) return nil unless @running.true? - ScheduledTask.execute(timeout_interval, args: [completion], &method(:timeout_task)) _success, value, reason = @executor.execute(self) if completion.try? self.value = value @@ -320,14 +307,5 @@ end nil end - - # @!visibility private - def timeout_task(completion) - return unless @running.true? - if completion.try? - schedule_next_task - observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new) - end - end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/tvar.rb new/lib/concurrent-ruby/concurrent/tvar.rb --- old/lib/concurrent-ruby/concurrent/tvar.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/tvar.rb 2022-03-22 01:03:48.000000000 +0100 @@ -15,7 +15,6 @@ # Create a new `TVar` with an initial value. def initialize(value) @value = value - @version = 0 @lock = Mutex.new end @@ -44,16 +43,6 @@ end # @!visibility private - def unsafe_version # :nodoc: - @version - end - - # @!visibility private - def unsafe_increment_version # :nodoc: - @version += 1 - end - - # @!visibility private def unsafe_lock # :nodoc: @lock end @@ -164,53 +153,39 @@ ABORTED = ::Object.new - ReadLogEntry = Struct.new(:tvar, :version) + OpenEntry = Struct.new(:value, :modified) AbortError = Class.new(StandardError) LeaveError = Class.new(StandardError) def initialize - @read_log = [] - @write_log = {} + @open_tvars = {} end def read(tvar) - Concurrent::abort_transaction unless valid? - - if @write_log.has_key? tvar - @write_log[tvar] - else - @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version)) - tvar.unsafe_value - end + entry = open(tvar) + entry.value end def write(tvar, value) - # Have we already written to this TVar? + entry = open(tvar) + entry.modified = true + entry.value = value + end - if @write_log.has_key? tvar - # Record the value written - @write_log[tvar] = value - else - # Try to lock the TVar + def open(tvar) + entry = @open_tvars[tvar] + unless entry unless tvar.unsafe_lock.try_lock - # Someone else is writing to this TVar - abort Concurrent::abort_transaction end - # Record the value written - - @write_log[tvar] = value - - # If we previously read from it, check the version hasn't changed - - @read_log.each do |log_entry| - if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version - Concurrent::abort_transaction - end - end + entry = OpenEntry.new(tvar.unsafe_value, false) + @open_tvars[tvar] = entry end + + entry end def abort @@ -218,32 +193,17 @@ end def commit - return false unless valid? - - @write_log.each_pair do |tvar, value| - tvar.unsafe_value = value - tvar.unsafe_increment_version - end - - unlock - - true - end - - def valid? - @read_log.each do |log_entry| - unless @write_log.has_key? log_entry.tvar - if log_entry.tvar.unsafe_version > log_entry.version - return false - end + @open_tvars.each do |tvar, entry| + if entry.modified + tvar.unsafe_value = entry.value end end - true + unlock end def unlock - @write_log.each_key do |tvar| + @open_tvars.each_key do |tvar| tvar.unsafe_lock.unlock end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb new/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb --- old/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb 2022-03-22 01:03:48.000000000 +0100 @@ -2,26 +2,64 @@ module Concurrent - class_definition = Class.new(Synchronization::LockableObject) do - def initialize - @last_time = Time.now.to_f - super() + # @!macro monotonic_get_time + # + # Returns the current time a tracked by the application monotonic clock. + # + # @param [Symbol] unit the time unit to be returned, can be either + # :float_second, :float_millisecond, :float_microsecond, :second, + # :millisecond, :microsecond, or :nanosecond default to :float_second. + # + # @return [Float] The current monotonic time since some unspecified + # starting point + # + # @!macro monotonic_clock_warning + if defined?(Process::CLOCK_MONOTONIC) + + def monotonic_time(unit = :float_second) + Process.clock_gettime(Process::CLOCK_MONOTONIC, unit) end - if defined?(Process::CLOCK_MONOTONIC) - # @!visibility private - def get_time - Process.clock_gettime(Process::CLOCK_MONOTONIC) - end - elsif Concurrent.on_jruby? - # @!visibility private - def get_time - java.lang.System.nanoTime() / 1_000_000_000.0 + elsif Concurrent.on_jruby? + + # @!visibility private + TIME_UNITS = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity + TIME_UNITS.merge!( + second: 1_000_000_000, + millisecond: 1_000_000, + microsecond: 1_000, + nanosecond: 1, + float_second: 1_000_000_000.0, + float_millisecond: 1_000_000.0, + float_microsecond: 1_000.0, + ) + TIME_UNITS.freeze + private_constant :TIME_UNITS + + def monotonic_time(unit = :float_second) + java.lang.System.nanoTime() / TIME_UNITS[unit] + end + + else + + class_definition = Class.new(Synchronization::LockableObject) do + def initialize + @last_time = Time.now.to_f + @time_units = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity + @time_units.merge!( + second: [nil, true], + millisecond: [1_000, true], + microsecond: [1_000_000, true], + nanosecond: [1_000_000_000, true], + float_second: [nil, false], + float_millisecond: [1_000.0, false], + float_microsecond: [1_000_000.0, false], + ) + super() end - else # @!visibility private - def get_time + def get_time(unit) synchronize do now = Time.now.to_f if @last_time < now @@ -29,30 +67,24 @@ else # clock has moved back in time @last_time += 0.000_001 end + scale, to_int = @time_units[unit] + now *= scale if scale + now = now.to_i if to_int + now end end - end - end - # Clock that cannot be set and represents monotonic time since - # some unspecified starting point. - # - # @!visibility private - GLOBAL_MONOTONIC_CLOCK = class_definition.new - private_constant :GLOBAL_MONOTONIC_CLOCK - - # @!macro monotonic_get_time - # - # Returns the current time a tracked by the application monotonic clock. - # - # @return [Float] The current monotonic time since some unspecified - # starting point - # - # @!macro monotonic_clock_warning - def monotonic_time - GLOBAL_MONOTONIC_CLOCK.get_time + # Clock that cannot be set and represents monotonic time since + # some unspecified starting point. + # + # @!visibility private + GLOBAL_MONOTONIC_CLOCK = class_definition.new + private_constant :GLOBAL_MONOTONIC_CLOCK + + def monotonic_time(unit = :float_second) + GLOBAL_MONOTONIC_CLOCK.get_time(unit) + end end - module_function :monotonic_time end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/utility/processor_counter.rb new/lib/concurrent-ruby/concurrent/utility/processor_counter.rb --- old/lib/concurrent-ruby/concurrent/utility/processor_counter.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/utility/processor_counter.rb 2022-03-22 01:03:48.000000000 +0100 @@ -79,47 +79,14 @@ def compute_processor_count if Concurrent.on_jruby? java.lang.Runtime.getRuntime.availableProcessors - elsif Etc.respond_to?(:nprocessors) && (nprocessor = Etc.nprocessors rescue nil) - nprocessor else - os_name = RbConfig::CONFIG["target_os"] - if os_name =~ /mingw|mswin/ - require 'win32ole' - result = WIN32OLE.connect("winmgmts://").ExecQuery( - "select NumberOfLogicalProcessors from Win32_Processor") - result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+) - elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0 - cpuinfo_count - elsif File.executable?("/usr/bin/nproc") - IO.popen("/usr/bin/nproc --all", &:read).to_i - elsif File.executable?("/usr/bin/hwprefs") - IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i - elsif File.executable?("/usr/sbin/psrinfo") - IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size - elsif File.executable?("/usr/sbin/ioscan") - IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size - elsif File.executable?("/usr/sbin/pmcycles") - IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n") - elsif File.executable?("/usr/sbin/lsdev") - IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n") - elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i - IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i - elsif File.executable?("/usr/sbin/sysctl") - IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i - elsif File.executable?("/sbin/sysctl") - IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i - else - # TODO (pitr-ch 05-Nov-2016): warn about failures - 1 - end + Etc.nprocessors end - rescue - return 1 end def compute_physical_processor_count ppc = case RbConfig::CONFIG["target_os"] - when /darwin1/ + when /darwin\d\d/ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i when /linux/ cores = {} # unique physical ID / core ID combinations diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent/version.rb new/lib/concurrent-ruby/concurrent/version.rb --- old/lib/concurrent-ruby/concurrent/version.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent/version.rb 2022-03-22 01:03:48.000000000 +0100 @@ -1,3 +1,3 @@ module Concurrent - VERSION = '1.1.9' + VERSION = '1.1.10' end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/concurrent-ruby/concurrent-ruby.rb new/lib/concurrent-ruby/concurrent-ruby.rb --- old/lib/concurrent-ruby/concurrent-ruby.rb 2021-06-05 14:47:58.000000000 +0200 +++ new/lib/concurrent-ruby/concurrent-ruby.rb 2022-03-22 01:03:48.000000000 +0100 @@ -1 +1,5 @@ -require_relative "./concurrent" +# This file is here so that there is a file with the same name as the gem that +# can be required by Bundler.require. Applications should normally +# require 'concurrent'. + +require_relative "concurrent" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2021-06-05 14:47:58.000000000 +0200 +++ new/metadata 2022-03-22 01:03:48.000000000 +0100 @@ -1,16 +1,16 @@ --- !ruby/object:Gem::Specification name: concurrent-ruby version: !ruby/object:Gem::Version - version: 1.1.9 + version: 1.1.10 platform: ruby authors: - Jerry D'Antonio - Petr Chalupa - The Ruby Concurrency Team -autorequire: +autorequire: bindir: bin cert_chain: [] -date: 2021-06-05 00:00:00.000000000 Z +date: 2022-03-22 00:00:00.000000000 Z dependencies: [] description: | Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more. @@ -170,7 +170,7 @@ metadata: source_code_uri: https://github.com/ruby-concurrency/concurrent-ruby changelog_uri: https://github.com/ruby-concurrency/concurrent-ruby/blob/master/CHANGELOG.md -post_install_message: +post_install_message: rdoc_options: [] require_paths: - lib/concurrent-ruby @@ -178,15 +178,15 @@ requirements: - - ">=" - !ruby/object:Gem::Version - version: 1.9.3 + version: '2.2' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] -rubygems_version: 3.2.3 -signing_key: +rubygems_version: 3.3.4 +signing_key: specification_version: 4 summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.