Merge pull request #5861 from Homebrew/dependabot/bundler/Library/Homebrew/concurrent-ruby-1.1.5

Bump concurrent-ruby from 1.1.4 to 1.1.5 in /Library/Homebrew
This commit is contained in:
Mike McQuaid 2019-03-11 13:16:59 +00:00 committed by GitHub
commit 0f07fe5c5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
126 changed files with 459 additions and 181 deletions

1
.gitignore vendored
View File

@ -88,6 +88,7 @@
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/synchronization/ **/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/synchronization/
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/thread_safe/ **/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/thread_safe/
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/utility/ **/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/utility/
**/vendor/bundle/ruby/*/gems/concurrent-ruby-*/lib/*/*.jar
**/vendor/bundle/ruby/*/gems/i18n-*/lib/i18n/tests* **/vendor/bundle/ruby/*/gems/i18n-*/lib/i18n/tests*
**/vendor/bundle/ruby/*/gems/thread_safe-*/lib/thread_safe/util **/vendor/bundle/ruby/*/gems/thread_safe-*/lib/thread_safe/util

View File

@ -8,7 +8,7 @@ GEM
tzinfo (~> 1.1) tzinfo (~> 1.1)
ast (2.4.0) ast (2.4.0)
backports (3.12.0) backports (3.12.0)
concurrent-ruby (1.1.4) concurrent-ruby (1.1.5)
connection_pool (2.2.2) connection_pool (2.2.2)
coveralls (0.8.22) coveralls (0.8.22)
json (>= 1.8, < 3) json (>= 1.8, < 3)

View File

@ -22,12 +22,12 @@ module Homebrew
ohai "cd #{HOMEBREW_LIBRARY_PATH}" ohai "cd #{HOMEBREW_LIBRARY_PATH}"
HOMEBREW_LIBRARY_PATH.cd do HOMEBREW_LIBRARY_PATH.cd do
ohai "bundle pristine"
safe_system "bundle", "pristine"
ohai "bundle install --standalone" ohai "bundle install --standalone"
safe_system "bundle", "install", "--standalone" safe_system "bundle", "install", "--standalone"
ohai "bundle pristine"
safe_system "bundle", "pristine"
ohai "git add vendor/bundle" ohai "git add vendor/bundle"
system "git", "add", "vendor/bundle" system "git", "add", "vendor/bundle"

View File

@ -3,7 +3,7 @@ require 'rbconfig'
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
ruby_version = RbConfig::CONFIG["ruby_version"] ruby_version = RbConfig::CONFIG["ruby_version"]
path = File.expand_path('..', __FILE__) path = File.expand_path('..', __FILE__)
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/concurrent-ruby-1.1.4/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/concurrent-ruby-1.1.5/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.6.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.6.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.11.3/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.11.3/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib"

View File

@ -1,4 +0,0 @@
module Concurrent
VERSION = '1.1.4'
EDGE_VERSION = '0.4.1'
end

View File

@ -21,8 +21,9 @@ module Concurrent
# @!macro internal_implementation_note # @!macro internal_implementation_note
ArrayImplementation = case ArrayImplementation = case
when Concurrent.on_cruby? when Concurrent.on_cruby?
# Because MRI never runs code in parallel, the existing # Array is thread-safe in practice because CRuby runs
# non-thread-safe structures should usually work fine. # threads one at a time and does not do context
# switching during the execution of C functions.
::Array ::Array
when Concurrent.on_jruby? when Concurrent.on_jruby?
@ -63,4 +64,3 @@ module Concurrent
end end
end end

View File

@ -333,6 +333,13 @@ module Concurrent
ivar ivar
end end
# Check whether the method is responsive
#
# @param [Symbol] method the method being called
def respond_to_missing?(method, include_private = false)
@delegate.respond_to?(method) || super
end
# Perform all enqueued tasks. # Perform all enqueued tasks.
# #
# This method must be called from within the executor. It must not be # This method must be called from within the executor. It must not be
@ -383,6 +390,13 @@ module Concurrent
ivar.wait ivar.wait
ivar ivar
end end
# Check whether the method is responsive
#
# @param [Symbol] method the method being called
def respond_to_missing?(method, include_private = false)
@delegate.respond_to?(method) || super
end
end end
private_constant :AwaitDelegator private_constant :AwaitDelegator

View File

@ -79,10 +79,10 @@ module Concurrent
# @!method value=(value) # @!method value=(value)
# @!macro atomic_fixnum_method_value_set # @!macro atomic_fixnum_method_value_set
# #
# @!method increment(delta) # @!method increment(delta = 1)
# @!macro atomic_fixnum_method_increment # @!macro atomic_fixnum_method_increment
# #
# @!method decrement(delta) # @!method decrement(delta = 1)
# @!macro atomic_fixnum_method_decrement # @!macro atomic_fixnum_method_decrement
# #
# @!method compare_and_set(expect, update) # @!method compare_and_set(expect, update)

View File

@ -53,7 +53,7 @@ module Concurrent
# @param [Node] head # @param [Node] head
# @return [true, false] # @return [true, false]
def empty?(head = self.head) def empty?(head = head())
head.equal? EMPTY head.equal? EMPTY
end end

View File

@ -48,11 +48,9 @@ module Concurrent
def post(delay, *args, &task) def post(delay, *args, &task)
raise ArgumentError.new('no block given') unless block_given? raise ArgumentError.new('no block given') unless block_given?
return false unless running? return false unless running?
opts = { opts = { executor: @task_executor,
executor: @task_executor, args: args,
args: args, timer_set: self }
timer_set: self
}
task = ScheduledTask.execute(delay, opts, &task) # may raise exception task = ScheduledTask.execute(delay, opts, &task) # may raise exception
task.unscheduled? ? false : task task.unscheduled? ? false : task
end end
@ -74,11 +72,11 @@ module Concurrent
# @param [Hash] opts the options to create the object with. # @param [Hash] opts the options to create the object with.
# @!visibility private # @!visibility private
def ns_initialize(opts) def ns_initialize(opts)
@queue = Collection::NonConcurrentPriorityQueue.new(order: :min) @queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
@task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
@timer_executor = SingleThreadExecutor.new @timer_executor = SingleThreadExecutor.new
@condition = Event.new @condition = Event.new
@ruby_pid = $$ # detects if Ruby has forked @ruby_pid = $$ # detects if Ruby has forked
self.auto_terminate = opts.fetch(:auto_terminate, true) self.auto_terminate = opts.fetch(:auto_terminate, true)
end end
@ -90,7 +88,7 @@ module Concurrent
# #
# @!visibility private # @!visibility private
def post_task(task) def post_task(task)
synchronize{ ns_post_task(task) } synchronize { ns_post_task(task) }
end end
# @!visibility private # @!visibility private
@ -98,7 +96,7 @@ module Concurrent
return false unless ns_running? return false unless ns_running?
ns_reset_if_forked ns_reset_if_forked
if (task.initial_delay) <= 0.01 if (task.initial_delay) <= 0.01
task.executor.post{ task.process_task } task.executor.post { task.process_task }
else else
@queue.push(task) @queue.push(task)
# only post the process method when the queue is empty # only post the process method when the queue is empty
@ -116,7 +114,7 @@ module Concurrent
# #
# @!visibility private # @!visibility private
def remove_task(task) def remove_task(task)
synchronize{ @queue.delete(task) } synchronize { @queue.delete(task) }
end end
# `ExecutorService` callback called during shutdown. # `ExecutorService` callback called during shutdown.
@ -148,7 +146,7 @@ module Concurrent
task = synchronize { @condition.reset; @queue.peek } task = synchronize { @condition.reset; @queue.peek }
break unless task break unless task
now = Concurrent.monotonic_time now = Concurrent.monotonic_time
diff = task.schedule_time - now diff = task.schedule_time - now
if diff <= 0 if diff <= 0
@ -165,7 +163,7 @@ module Concurrent
# queue now must have the same pop time, or a closer one, as # queue now must have the same pop time, or a closer one, as
# when we peeked). # when we peeked).
task = synchronize { @queue.pop } task = synchronize { @queue.pop }
task.executor.post{ task.process_task } task.executor.post { task.process_task }
else else
@condition.wait([diff, 60].min) @condition.wait([diff, 60].min)
end end

View File

@ -15,8 +15,9 @@ module Concurrent
# @!macro internal_implementation_note # @!macro internal_implementation_note
HashImplementation = case HashImplementation = case
when Concurrent.on_cruby? when Concurrent.on_cruby?
# Because MRI never runs code in parallel, the existing # Hash is thread-safe in practice because CRuby runs
# non-thread-safe structures should usually work fine. # threads one at a time and does not do context
# switching during the execution of C functions.
::Hash ::Hash
when Concurrent.on_jruby? when Concurrent.on_jruby?
@ -56,4 +57,3 @@ module Concurrent
end end
end end

View File

@ -43,6 +43,7 @@ module Concurrent
# new. # new.
module FactoryMethods module FactoryMethods
extend ReInclude extend ReInclude
extend self
module Configuration module Configuration
# @return [Executor, :io, :fast] the executor which is used when none is supplied # @return [Executor, :io, :fast] the executor which is used when none is supplied
@ -92,16 +93,14 @@ module Concurrent
future_on(default_executor, *args, &task) future_on(default_executor, *args, &task)
end end
# @!macro promises.future-on1 # Constructs new Future which will be resolved after block is evaluated on default executor.
# Constructs new Future which will be resolved after block is evaluated on default executor.
# Evaluation begins immediately. # Evaluation begins immediately.
# #
# @!macro promises.future-on2 # @!macro promises.param.default_executor
# @!macro promises.param.default_executor # @!macro promises.param.args
# @!macro promises.param.args # @yield [*args] to the task.
# @yield [*args] to the task. # @!macro promise.param.task-future
# @!macro promise.param.task-future # @return [Future]
# @return [Future]
def future_on(default_executor, *args, &task) def future_on(default_executor, *args, &task)
ImmediateEventPromise.new(default_executor).future.then(*args, &task) ImmediateEventPromise.new(default_executor).future.then(*args, &task)
end end
@ -109,6 +108,9 @@ module Concurrent
# Creates resolved future with will be either fulfilled with the given value or rejection with # Creates resolved future with will be either fulfilled with the given value or rejection with
# the given reason. # the given reason.
# #
# @param [true, false] fulfilled
# @param [Object] value
# @param [Object] reason
# @!macro promises.param.default_executor # @!macro promises.param.default_executor
# @return [Future] # @return [Future]
def resolved_future(fulfilled, value, reason, default_executor = self.default_executor) def resolved_future(fulfilled, value, reason, default_executor = self.default_executor)
@ -118,6 +120,7 @@ module Concurrent
# Creates resolved future with will be fulfilled with the given value. # Creates resolved future with will be fulfilled with the given value.
# #
# @!macro promises.param.default_executor # @!macro promises.param.default_executor
# @param [Object] value
# @return [Future] # @return [Future]
def fulfilled_future(value, default_executor = self.default_executor) def fulfilled_future(value, default_executor = self.default_executor)
resolved_future true, value, nil, default_executor resolved_future true, value, nil, default_executor
@ -126,6 +129,7 @@ module Concurrent
# Creates resolved future with will be rejected with the given reason. # Creates resolved future with will be rejected with the given reason.
# #
# @!macro promises.param.default_executor # @!macro promises.param.default_executor
# @param [Object] reason
# @return [Future] # @return [Future]
def rejected_future(reason, default_executor = self.default_executor) def rejected_future(reason, default_executor = self.default_executor)
resolved_future false, nil, reason, default_executor resolved_future false, nil, reason, default_executor
@ -146,23 +150,23 @@ module Concurrent
# @!macro promises.param.default_executor # @!macro promises.param.default_executor
# @return [Event, Future] # @return [Event, Future]
# #
# @overload create(nil, default_executor = self.default_executor) # @overload make_future(nil, default_executor = self.default_executor)
# @param [nil] nil # @param [nil] nil
# @return [Event] resolved event. # @return [Event] resolved event.
# #
# @overload create(a_future, default_executor = self.default_executor) # @overload make_future(a_future, default_executor = self.default_executor)
# @param [Future] a_future # @param [Future] a_future
# @return [Future] a future which will be resolved when a_future is. # @return [Future] a future which will be resolved when a_future is.
# #
# @overload create(an_event, default_executor = self.default_executor) # @overload make_future(an_event, default_executor = self.default_executor)
# @param [Event] an_event # @param [Event] an_event
# @return [Event] an event which will be resolved when an_event is. # @return [Event] an event which will be resolved when an_event is.
# #
# @overload create(exception, default_executor = self.default_executor) # @overload make_future(exception, default_executor = self.default_executor)
# @param [Exception] exception # @param [Exception] exception
# @return [Future] a rejected future with the exception as its reason. # @return [Future] a rejected future with the exception as its reason.
# #
# @overload create(value, default_executor = self.default_executor) # @overload make_future(value, default_executor = self.default_executor)
# @param [Object] value when none of the above overloads fits # @param [Object] value when none of the above overloads fits
# @return [Future] a fulfilled future with the value. # @return [Future] a fulfilled future with the value.
def make_future(argument = nil, default_executor = self.default_executor) def make_future(argument = nil, default_executor = self.default_executor)
@ -180,34 +184,53 @@ module Concurrent
end end
# @!macro promises.shortcut.on # @!macro promises.shortcut.on
# @return [Future] # @return [Future, Event]
def delay(*args, &task) def delay(*args, &task)
delay_on default_executor, *args, &task delay_on default_executor, *args, &task
end end
# @!macro promises.future-on1 # Creates new event or future which is resolved only after it is touched,
# The task will be evaluated only after the future is touched, see {AbstractEventFuture#touch} # see {Concurrent::AbstractEventFuture#touch}.
# #
# @!macro promises.future-on2 # @!macro promises.param.default_executor
# @overload delay_on(default_executor, *args, &task)
# If task is provided it returns a {Future} representing the result of the task.
# @!macro promises.param.args
# @yield [*args] to the task.
# @!macro promise.param.task-future
# @return [Future]
# @overload delay_on(default_executor)
# If no task is provided, it returns an {Event}
# @return [Event]
def delay_on(default_executor, *args, &task) def delay_on(default_executor, *args, &task)
DelayPromise.new(default_executor).event.chain(*args, &task) event = DelayPromise.new(default_executor).event
task ? event.chain(*args, &task) : event
end end
# @!macro promises.shortcut.on # @!macro promises.shortcut.on
# @return [Future] # @return [Future, Event]
def schedule(intended_time, *args, &task) def schedule(intended_time, *args, &task)
schedule_on default_executor, intended_time, *args, &task schedule_on default_executor, intended_time, *args, &task
end end
# @!macro promises.future-on1 # Creates new event or future which is resolved in intended_time.
# The task is planned for execution in intended_time.
# #
# @!macro promises.future-on2 # @!macro promises.param.default_executor
# @!macro promises.param.intended_time # @!macro promises.param.intended_time
# @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds. # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds.
# `Time` means to run on `intended_time`. # `Time` means to run on `intended_time`.
# @overload schedule_on(default_executor, intended_time, *args, &task)
# If task is provided it returns a {Future} representing the result of the task.
# @!macro promises.param.args
# @yield [*args] to the task.
# @!macro promise.param.task-future
# @return [Future]
# @overload schedule_on(default_executor, intended_time)
# If no task is provided, it returns an {Event}
# @return [Event]
def schedule_on(default_executor, intended_time, *args, &task) def schedule_on(default_executor, intended_time, *args, &task)
ScheduledPromise.new(default_executor, intended_time).event.chain(*args, &task) event = ScheduledPromise.new(default_executor, intended_time).event
task ? event.chain(*args, &task) : event
end end
# @!macro promises.shortcut.on # @!macro promises.shortcut.on
@ -259,7 +282,7 @@ module Concurrent
# Creates new future which is resolved after first futures_and_or_events is resolved. # Creates new future which is resolved after first futures_and_or_events is resolved.
# Its result equals result of the first resolved future. # Its result equals result of the first resolved future.
# @!macro promises.any-touch # @!macro promises.any-touch
# If resolved it does not propagate {AbstractEventFuture#touch}, leaving delayed # If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed
# futures un-executed if they are not required any more. # futures un-executed if they are not required any more.
# @!macro promises.event-conversion # @!macro promises.event-conversion
# #
@ -311,7 +334,7 @@ module Concurrent
end end
module InternalStates module InternalStates
# @private # @!visibility private
class State class State
def resolved? def resolved?
raise NotImplementedError raise NotImplementedError
@ -322,9 +345,7 @@ module Concurrent
end end
end end
private_constant :State # @!visibility private
# @private
class Pending < State class Pending < State
def resolved? def resolved?
false false
@ -335,9 +356,11 @@ module Concurrent
end end
end end
private_constant :Pending # @!visibility private
class Reserved < Pending
end
# @private # @!visibility private
class ResolvedWithResult < State class ResolvedWithResult < State
def resolved? def resolved?
true true
@ -368,9 +391,7 @@ module Concurrent
end end
end end
private_constant :ResolvedWithResult # @!visibility private
# @private
class Fulfilled < ResolvedWithResult class Fulfilled < ResolvedWithResult
def initialize(value) def initialize(value)
@ -398,18 +419,14 @@ module Concurrent
end end
end end
private_constant :Fulfilled # @!visibility private
# @private
class FulfilledArray < Fulfilled class FulfilledArray < Fulfilled
def apply(args, block) def apply(args, block)
block.call(*value, *args) block.call(*value, *args)
end end
end end
private_constant :FulfilledArray # @!visibility private
# @private
class Rejected < ResolvedWithResult class Rejected < ResolvedWithResult
def initialize(reason) def initialize(reason)
@Reason = reason @Reason = reason
@ -436,9 +453,7 @@ module Concurrent
end end
end end
private_constant :Rejected # @!visibility private
# @private
class PartiallyRejected < ResolvedWithResult class PartiallyRejected < ResolvedWithResult
def initialize(value, reason) def initialize(value, reason)
super() super()
@ -467,24 +482,38 @@ module Concurrent
end end
end end
private_constant :PartiallyRejected # @!visibility private
PENDING = Pending.new
PENDING = Pending.new # @!visibility private
RESERVED = Reserved.new
# @!visibility private
RESOLVED = Fulfilled.new(nil) RESOLVED = Fulfilled.new(nil)
def RESOLVED.to_sym def RESOLVED.to_sym
:resolved :resolved
end end
private_constant :PENDING, :RESOLVED
end end
private_constant :InternalStates private_constant :InternalStates
# @!macro promises.shortcut.event-future
# @see Event#$0
# @see Future#$0
# @!macro promises.param.timeout
# @param [Numeric] timeout the maximum time in second to wait.
# @!macro promises.warn.blocks
# @note This function potentially blocks current thread until the Future is resolved.
# Be careful it can deadlock. Try to chain instead.
# Common ancestor of {Event} and {Future} classes, many shared methods are defined here. # Common ancestor of {Event} and {Future} classes, many shared methods are defined here.
class AbstractEventFuture < Synchronization::Object class AbstractEventFuture < Synchronization::Object
safe_initialization! safe_initialization!
private(*attr_atomic(:internal_state) - [:internal_state]) attr_atomic(:internal_state)
private :internal_state=, :swap_internal_state, :compare_and_set_internal_state, :update_internal_state
# @!method internal_state
# @!visibility private
include InternalStates include InternalStates
@ -501,17 +530,6 @@ module Concurrent
private :initialize private :initialize
# @!macro promises.shortcut.event-future
# @see Event#$0
# @see Future#$0
# @!macro promises.param.timeout
# @param [Numeric] timeout the maximum time in second to wait.
# @!macro promises.warn.blocks
# @note This function potentially blocks current thread until the Future is resolved.
# Be careful it can deadlock. Try to chain instead.
# Returns its state. # Returns its state.
# @return [Symbol] # @return [Symbol]
# #
@ -545,7 +563,7 @@ module Concurrent
end end
# @!macro promises.touches # @!macro promises.touches
# Calls {AbstractEventFuture#touch}. # Calls {Concurrent::AbstractEventFuture#touch}.
# @!macro promises.method.wait # @!macro promises.method.wait
# Wait (block the Thread) until receiver is {#resolved?}. # Wait (block the Thread) until receiver is {#resolved?}.
@ -553,7 +571,7 @@ module Concurrent
# #
# @!macro promises.warn.blocks # @!macro promises.warn.blocks
# @!macro promises.param.timeout # @!macro promises.param.timeout
# @return [Future, true, false] self implies timeout was not used, true implies timeout was used # @return [self, true, false] self implies timeout was not used, true implies timeout was used
# and it was resolved, false implies it was not resolved within timeout. # and it was resolved, false implies it was not resolved within timeout.
def wait(timeout = nil) def wait(timeout = nil)
result = wait_until_resolved(timeout) result = wait_until_resolved(timeout)
@ -590,7 +608,7 @@ module Concurrent
# @yield [fulfilled, value, reason, *args] to the task. # @yield [fulfilled, value, reason, *args] to the task.
# @yieldparam [true, false] fulfilled # @yieldparam [true, false] fulfilled
# @yieldparam [Object] value # @yieldparam [Object] value
# @yieldparam [Exception] reason # @yieldparam [Object] reason
def chain_on(executor, *args, &task) def chain_on(executor, *args, &task)
ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
end end
@ -631,7 +649,7 @@ module Concurrent
# @yield [fulfilled, value, reason, *args] to the callback. # @yield [fulfilled, value, reason, *args] to the callback.
# @yieldparam [true, false] fulfilled # @yieldparam [true, false] fulfilled
# @yieldparam [Object] value # @yieldparam [Object] value
# @yieldparam [Exception] reason # @yieldparam [Object] reason
def on_resolution!(*args, &callback) def on_resolution!(*args, &callback)
add_callback :callback_on_resolution, args, callback add_callback :callback_on_resolution, args, callback
end end
@ -649,7 +667,7 @@ module Concurrent
# @yield [fulfilled, value, reason, *args] to the callback. # @yield [fulfilled, value, reason, *args] to the callback.
# @yieldparam [true, false] fulfilled # @yieldparam [true, false] fulfilled
# @yieldparam [Object] value # @yieldparam [Object] value
# @yieldparam [Exception] reason # @yieldparam [Object] reason
def on_resolution_using(executor, *args, &callback) def on_resolution_using(executor, *args, &callback)
add_callback :async_callback_on_resolution, executor, args, callback add_callback :async_callback_on_resolution, executor, args, callback
end end
@ -665,8 +683,8 @@ module Concurrent
end end
# @!visibility private # @!visibility private
def resolve_with(state, raise_on_reassign = true) def resolve_with(state, raise_on_reassign = true, reserved = false)
if compare_and_set_internal_state(PENDING, state) if compare_and_set_internal_state(reserved ? RESERVED : PENDING, state)
# go to synchronized block only if there were waiting threads # go to synchronized block only if there were waiting threads
@Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0 @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
call_callbacks state call_callbacks state
@ -719,6 +737,12 @@ module Concurrent
add_callback(:callback_clear_delayed_node, node) add_callback(:callback_clear_delayed_node, node)
end end
# @!visibility private
def with_hidden_resolvable
# TODO (pitr-ch 10-Dec-2018): documentation, better name if in edge
self
end
private private
def add_callback(method, *args) def add_callback(method, *args)
@ -907,9 +931,17 @@ module Concurrent
# @!macro promises.warn.blocks # @!macro promises.warn.blocks
# @!macro promises.warn.nil # @!macro promises.warn.nil
# @!macro promises.param.timeout # @!macro promises.param.timeout
# @return [Object, nil] the value of the Future when fulfilled, nil on timeout or rejection. # @!macro promises.param.timeout_value
def value(timeout = nil) # @param [Object] timeout_value a value returned by the method when it times out
internal_state.value if wait_until_resolved timeout # @return [Object, nil, timeout_value] the value of the Future when fulfilled,
# timeout_value on timeout,
# nil on rejection.
def value(timeout = nil, timeout_value = nil)
if wait_until_resolved timeout
internal_state.value
else
timeout_value
end
end end
# Returns reason of future's rejection. # Returns reason of future's rejection.
@ -918,9 +950,14 @@ module Concurrent
# @!macro promises.warn.blocks # @!macro promises.warn.blocks
# @!macro promises.warn.nil # @!macro promises.warn.nil
# @!macro promises.param.timeout # @!macro promises.param.timeout
# @return [Exception, nil] nil on timeout or fulfillment. # @!macro promises.param.timeout_value
def reason(timeout = nil) # @return [Object, timeout_value] the reason, or timeout_value on timeout, or nil on fulfillment.
internal_state.reason if wait_until_resolved timeout def reason(timeout = nil, timeout_value = nil)
if wait_until_resolved timeout
internal_state.reason
else
timeout_value
end
end end
# Returns triplet fulfilled?, value, reason. # Returns triplet fulfilled?, value, reason.
@ -928,7 +965,7 @@ module Concurrent
# #
# @!macro promises.warn.blocks # @!macro promises.warn.blocks
# @!macro promises.param.timeout # @!macro promises.param.timeout
# @return [Array(Boolean, Object, Exception), nil] triplet of fulfilled?, value, reason, or nil # @return [Array(Boolean, Object, Object), nil] triplet of fulfilled?, value, reason, or nil
# on timeout. # on timeout.
def result(timeout = nil) def result(timeout = nil)
internal_state.result if wait_until_resolved timeout internal_state.result if wait_until_resolved timeout
@ -942,26 +979,40 @@ module Concurrent
end end
# @!macro promises.method.value # @!macro promises.method.value
# @return [Object, nil] the value of the Future when fulfilled, nil on timeout. # @return [Object, nil, timeout_value] the value of the Future when fulfilled,
# or nil on rejection,
# or timeout_value on timeout.
# @raise [Exception] {#reason} on rejection # @raise [Exception] {#reason} on rejection
def value!(timeout = nil) def value!(timeout = nil, timeout_value = nil)
internal_state.value if wait_until_resolved! timeout if wait_until_resolved! timeout
internal_state.value
else
timeout_value
end
end end
# Allows rejected Future to be risen with `raise` method. # Allows rejected Future to be risen with `raise` method.
# If the reason is not an exception `Runtime.new(reason)` is returned.
#
# @example # @example
# raise Promises.rejected_future(StandardError.new("boom")) # raise Promises.rejected_future(StandardError.new("boom"))
# @raise [StandardError] when raising not rejected future # raise Promises.rejected_future("or just boom")
# @raise [Concurrent::Error] when raising not rejected future
# @return [Exception] # @return [Exception]
def exception(*args) def exception(*args)
raise Concurrent::Error, 'it is not rejected' unless rejected? raise Concurrent::Error, 'it is not rejected' unless rejected?
raise ArgumentError unless args.size <= 1
reason = Array(internal_state.reason).flatten.compact reason = Array(internal_state.reason).flatten.compact
if reason.size > 1 if reason.size > 1
ex = Concurrent::MultipleErrors.new reason ex = Concurrent::MultipleErrors.new reason
ex.set_backtrace(caller) ex.set_backtrace(caller)
ex ex
else else
ex = reason[0].clone.exception(*args) ex = if reason[0].respond_to? :exception
reason[0].exception(*args)
else
RuntimeError.new(reason[0]).exception(*args)
end
ex.set_backtrace Array(ex.backtrace) + caller ex.set_backtrace Array(ex.backtrace) + caller
ex ex
end end
@ -1133,14 +1184,20 @@ module Concurrent
# will become reason of the returned future. # will become reason of the returned future.
# #
# @return [Future] # @return [Future]
# @param [#call(value)] run_test
# an object which when called returns either Future to keep running with
# or nil, then the run completes with the value.
# The run_test can be used to extract the Future from deeper structure,
# or to distinguish Future which is a resulting value from a future
# which is suppose to continue running.
# @example # @example
# body = lambda do |v| # body = lambda do |v|
# v += 1 # v += 1
# v < 5 ? Promises.future(v, &body) : v # v < 5 ? Promises.future(v, &body) : v
# end # end
# Promises.future(0, &body).run.value! # => 5 # Promises.future(0, &body).run.value! # => 5
def run def run(run_test = method(:run_test))
RunFuturePromise.new_blocked_by1(self, @DefaultExecutor).future RunFuturePromise.new_blocked_by1(self, @DefaultExecutor, run_test).future
end end
# @!visibility private # @!visibility private
@ -1163,13 +1220,34 @@ module Concurrent
self self
end end
# @return [String] Short string representation.
def to_s
if resolved?
format '%s with %s>', super[0..-2], (fulfilled? ? value : reason).inspect
else
super
end
end
alias_method :inspect, :to_s
private private
def run_test(v)
v if v.is_a?(Future)
end
def rejected_resolution(raise_on_reassign, state) def rejected_resolution(raise_on_reassign, state)
if raise_on_reassign if raise_on_reassign
raise Concurrent::MultipleAssignmentError.new( if internal_state == RESERVED
"Future can be resolved only once. It's #{result}, trying to set #{state.result}.", raise Concurrent::MultipleAssignmentError.new(
current_result: result, new_result: state.result) "Future can be resolved only once. It is already reserved.")
else
raise Concurrent::MultipleAssignmentError.new(
"Future can be resolved only once. It's #{result}, trying to set #{state.result}.",
current_result: result,
new_result: state.result)
end
end end
return false return false
end end
@ -1206,15 +1284,15 @@ module Concurrent
end end
# Marker module of Future, Event resolved manually by user. # Marker module of Future, Event resolved manually.
module Resolvable module Resolvable
include InternalStates
end end
# A Event which can be resolved by user. # A Event which can be resolved by user.
class ResolvableEvent < Event class ResolvableEvent < Event
include Resolvable include Resolvable
# @!macro raise_on_reassign # @!macro raise_on_reassign
# @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true. # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true.
@ -1227,8 +1305,13 @@ module Concurrent
# Makes the event resolved, which triggers all dependent futures. # Makes the event resolved, which triggers all dependent futures.
# #
# @!macro promise.param.raise_on_reassign # @!macro promise.param.raise_on_reassign
def resolve(raise_on_reassign = true) # @!macro promise.param.reserved
resolve_with RESOLVED, raise_on_reassign # @param [true, false] reserved
# Set to true if the resolvable is {#reserve}d by you,
# marks resolution of reserved resolvable events and futures explicitly.
# Advanced feature, ignore unless you use {Resolvable#reserve} from edge.
def resolve(raise_on_reassign = true, reserved = false)
resolve_with RESOLVED, raise_on_reassign, reserved
end end
# Creates new event wrapping receiver, effectively hiding the resolve method. # Creates new event wrapping receiver, effectively hiding the resolve method.
@ -1237,6 +1320,23 @@ module Concurrent
def with_hidden_resolvable def with_hidden_resolvable
@with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event
end end
# Behaves as {AbstractEventFuture#wait} but has one additional optional argument
# resolve_on_timeout.
#
# @param [true, false] resolve_on_timeout
# If it times out and the argument is true it will also resolve the event.
# @return [self, true, false]
# @see AbstractEventFuture#wait
def wait(timeout = nil, resolve_on_timeout = false)
super(timeout) or if resolve_on_timeout
# if it fails to resolve it was resolved in the meantime
# so return true as if there was no timeout
!resolve(false)
else
false
end
end
end end
# A Future which can be resolved by user. # A Future which can be resolved by user.
@ -1246,29 +1346,38 @@ module Concurrent
# Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`, # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`,
# which triggers all dependent futures. # which triggers all dependent futures.
# #
# @param [true, false] fulfilled
# @param [Object] value
# @param [Object] reason
# @!macro promise.param.raise_on_reassign # @!macro promise.param.raise_on_reassign
def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true) # @!macro promise.param.reserved
resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign) def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true, reserved = false)
resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign, reserved)
end end
# Makes the future fulfilled with `value`, # Makes the future fulfilled with `value`,
# which triggers all dependent futures. # which triggers all dependent futures.
# #
# @param [Object] value
# @!macro promise.param.raise_on_reassign # @!macro promise.param.raise_on_reassign
def fulfill(value, raise_on_reassign = true) # @!macro promise.param.reserved
promise.fulfill(value, raise_on_reassign) def fulfill(value, raise_on_reassign = true, reserved = false)
resolve_with Fulfilled.new(value), raise_on_reassign, reserved
end end
# Makes the future rejected with `reason`, # Makes the future rejected with `reason`,
# which triggers all dependent futures. # which triggers all dependent futures.
# #
# @param [Object] reason
# @!macro promise.param.raise_on_reassign # @!macro promise.param.raise_on_reassign
def reject(reason, raise_on_reassign = true) # @!macro promise.param.reserved
promise.reject(reason, raise_on_reassign) def reject(reason, raise_on_reassign = true, reserved = false)
resolve_with Rejected.new(reason), raise_on_reassign, reserved
end end
# Evaluates the block and sets its result as future's value fulfilling, if the block raises # Evaluates the block and sets its result as future's value fulfilling, if the block raises
# an exception the future rejects with it. # an exception the future rejects with it.
#
# @yield [*args] to the block. # @yield [*args] to the block.
# @yieldreturn [Object] value # @yieldreturn [Object] value
# @return [self] # @return [self]
@ -1278,6 +1387,7 @@ module Concurrent
# Evaluates the block and sets its result as future's value fulfilling, if the block raises # Evaluates the block and sets its result as future's value fulfilling, if the block raises
# an exception the future rejects with it. # an exception the future rejects with it.
#
# @yield [*args] to the block. # @yield [*args] to the block.
# @yieldreturn [Object] value # @yieldreturn [Object] value
# @return [self] # @return [self]
@ -1286,6 +1396,135 @@ module Concurrent
promise.evaluate_to(*args, block).wait! promise.evaluate_to(*args, block).wait!
end end
# @!macro promises.resolvable.resolve_on_timeout
# @param [::Array(true, Object, nil), ::Array(false, nil, Exception), nil] resolve_on_timeout
# If it times out and the argument is not nil it will also resolve the future
# to the provided resolution.
# Behaves as {AbstractEventFuture#wait} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [self, true, false]
# @see AbstractEventFuture#wait
def wait(timeout = nil, resolve_on_timeout = nil)
super(timeout) or if resolve_on_timeout
# if it fails to resolve it was resolved in the meantime
# so return true as if there was no timeout
!resolve(*resolve_on_timeout, false)
else
false
end
end
# Behaves as {Future#wait!} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [self, true, false]
# @raise [Exception] {#reason} on rejection
# @see Future#wait!
def wait!(timeout = nil, resolve_on_timeout = nil)
super(timeout) or if resolve_on_timeout
if resolve(*resolve_on_timeout, false)
false
else
# if it fails to resolve it was resolved in the meantime
# so return true as if there was no timeout
raise self if rejected?
true
end
else
false
end
end
# Behaves as {Future#value} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [Object, timeout_value, nil]
# @see Future#value
def value(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
if wait_until_resolved timeout
internal_state.value
else
if resolve_on_timeout
unless resolve(*resolve_on_timeout, false)
# if it fails to resolve it was resolved in the meantime
# so return value as if there was no timeout
return internal_state.value
end
end
timeout_value
end
end
# Behaves as {Future#value!} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [Object, timeout_value, nil]
# @raise [Exception] {#reason} on rejection
# @see Future#value!
def value!(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
if wait_until_resolved! timeout
internal_state.value
else
if resolve_on_timeout
unless resolve(*resolve_on_timeout, false)
# if it fails to resolve it was resolved in the meantime
# so return value as if there was no timeout
raise self if rejected?
return internal_state.value
end
end
timeout_value
end
end
# Behaves as {Future#reason} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [Exception, timeout_value, nil]
# @see Future#reason
def reason(timeout = nil, timeout_value = nil, resolve_on_timeout = nil)
if wait_until_resolved timeout
internal_state.reason
else
if resolve_on_timeout
unless resolve(*resolve_on_timeout, false)
# if it fails to resolve it was resolved in the meantime
# so return value as if there was no timeout
return internal_state.reason
end
end
timeout_value
end
end
# Behaves as {Future#result} but has one additional optional argument
# resolve_on_timeout.
#
# @!macro promises.resolvable.resolve_on_timeout
# @return [::Array(Boolean, Object, Exception), nil]
# @see Future#result
def result(timeout = nil, resolve_on_timeout = nil)
if wait_until_resolved timeout
internal_state.result
else
if resolve_on_timeout
unless resolve(*resolve_on_timeout, false)
# if it fails to resolve it was resolved in the meantime
# so return value as if there was no timeout
internal_state.result
end
end
# otherwise returns nil
end
end
# Creates new future wrapping receiver, effectively hiding the resolve method and similar. # Creates new future wrapping receiver, effectively hiding the resolve method and similar.
# #
# @return [Future] # @return [Future]
@ -1358,14 +1597,6 @@ module Concurrent
super ResolvableFuture.new(self, default_executor) super ResolvableFuture.new(self, default_executor)
end end
def fulfill(value, raise_on_reassign)
resolve_with Fulfilled.new(value), raise_on_reassign
end
def reject(reason, raise_on_reassign)
resolve_with Rejected.new(reason), raise_on_reassign
end
public :evaluate_to public :evaluate_to
end end
@ -1615,7 +1846,7 @@ module Concurrent
value = internal_state.value value = internal_state.value
case value case value
when Future, Event when AbstractEventFuture
add_delayed_of value add_delayed_of value
value.add_callback_notify_blocked self, nil value.add_callback_notify_blocked self, nil
countdown countdown
@ -1651,12 +1882,10 @@ module Concurrent
value = internal_state.value value = internal_state.value
case value case value
when Future when AbstractEventFuture
add_delayed_of value add_delayed_of value
value.add_callback_notify_blocked self, nil value.add_callback_notify_blocked self, nil
countdown countdown
when Event
evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
else else
evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" }) evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
end end
@ -1670,8 +1899,9 @@ module Concurrent
private private
def initialize(delayed, blockers_count, default_executor) def initialize(delayed, blockers_count, default_executor, run_test)
super delayed, 1, Future.new(self, default_executor) super delayed, 1, Future.new(self, default_executor)
@RunTest = run_test
end end
def process_on_blocker_resolution(future, index) def process_on_blocker_resolution(future, index)
@ -1682,11 +1912,12 @@ module Concurrent
return 0 return 0
end end
value = internal_state.value value = internal_state.value
case value continuation_future = @RunTest.call value
when Future
add_delayed_of value if continuation_future
value.add_callback_notify_blocked self, nil add_delayed_of continuation_future
continuation_future.add_callback_notify_blocked self, nil
else else
resolve_with internal_state resolve_with internal_state
end end
@ -1805,23 +2036,6 @@ module Concurrent
class AbstractAnyPromise < BlockedPromise class AbstractAnyPromise < BlockedPromise
end end
class AnyResolvedFuturePromise < AbstractAnyPromise
private
def initialize(delayed, blockers_count, default_executor)
super delayed, blockers_count, Future.new(self, default_executor)
end
def resolvable?(countdown, future, index)
true
end
def on_resolvable(resolved_future, index)
resolve_with resolved_future.internal_state, false
end
end
class AnyResolvedEventPromise < AbstractAnyPromise class AnyResolvedEventPromise < AbstractAnyPromise
private private
@ -1839,6 +2053,23 @@ module Concurrent
end end
end end
class AnyResolvedFuturePromise < AbstractAnyPromise
private
def initialize(delayed, blockers_count, default_executor)
super delayed, blockers_count, Future.new(self, default_executor)
end
def resolvable?(countdown, future, index)
true
end
def on_resolvable(resolved_future, index)
resolve_with resolved_future.internal_state, false
end
end
class AnyFulfilledFuturePromise < AnyResolvedFuturePromise class AnyFulfilledFuturePromise < AnyResolvedFuturePromise
private private

View File

@ -152,6 +152,7 @@ module Concurrent
end end
end end
clazz.class_exec(&block) unless block.nil? clazz.class_exec(&block) unless block.nil?
clazz.singleton_class.send :alias_method, :[], :new
clazz clazz
end end
end end

View File

@ -1,5 +1,7 @@
module Concurrent module Concurrent
module Synchronization module Synchronization
# @!visibility private
# TODO (pitr-ch 04-Dec-2016): should be in edge # TODO (pitr-ch 04-Dec-2016): should be in edge
class Condition < LockableObject class Condition < LockableObject
safe_initialization! safe_initialization!

View File

@ -3,6 +3,7 @@ module Concurrent
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
# @!visibility private
module JRubyAttrVolatile module JRubyAttrVolatile
def self.included(base) def self.included(base)
base.extend(ClassMethods) base.extend(ClassMethods)

View File

@ -1,5 +1,7 @@
module Concurrent module Concurrent
module Synchronization module Synchronization
# @!visibility private
# TODO (pitr-ch 04-Dec-2016): should be in edge # TODO (pitr-ch 04-Dec-2016): should be in edge
class Lock < LockableObject class Lock < LockableObject
# TODO use JavaReentrantLock on JRuby # TODO use JavaReentrantLock on JRuby

Some files were not shown because too many files have changed in this diff Show More