This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
require 'thread'
|
||||
require 'concurrent/constants'
|
||||
require 'concurrent/errors'
|
||||
require 'concurrent/ivar'
|
||||
require 'concurrent/executor/safe_task_executor'
|
||||
|
||||
require 'concurrent/options'
|
||||
|
||||
# TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc.
|
||||
|
||||
|
||||
module Concurrent
|
||||
|
||||
# {include:file:docs-source/future.md}
|
||||
#
|
||||
# @!macro copy_options
|
||||
#
|
||||
# @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module
|
||||
# @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function
|
||||
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
|
||||
class Future < IVar
|
||||
|
||||
# Create a new `Future` in the `:unscheduled` state.
|
||||
#
|
||||
# @yield the asynchronous operation to perform
|
||||
#
|
||||
# @!macro executor_and_deref_options
|
||||
#
|
||||
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
||||
# block on execution
|
||||
#
|
||||
# @raise [ArgumentError] if no block is given
|
||||
def initialize(opts = {}, &block)
|
||||
raise ArgumentError.new('no block given') unless block_given?
|
||||
super(NULL, opts.merge(__task_from_block__: block), &nil)
|
||||
end
|
||||
|
||||
# Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and
|
||||
# passes the block to a new thread/thread pool for eventual execution.
|
||||
# Does nothing if the `Future` is in any state other than `:unscheduled`.
|
||||
#
|
||||
# @return [Future] a reference to `self`
|
||||
#
|
||||
# @example Instance and execute in separate steps
|
||||
# future = Concurrent::Future.new{ sleep(1); 42 }
|
||||
# future.state #=> :unscheduled
|
||||
# future.execute
|
||||
# future.state #=> :pending
|
||||
#
|
||||
# @example Instance and execute in one line
|
||||
# future = Concurrent::Future.new{ sleep(1); 42 }.execute
|
||||
# future.state #=> :pending
|
||||
def execute
|
||||
if compare_and_set_state(:pending, :unscheduled)
|
||||
@executor.post{ safe_execute(@task, @args) }
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# Create a new `Future` object with the given block, execute it, and return the
|
||||
# `:pending` object.
|
||||
#
|
||||
# @yield the asynchronous operation to perform
|
||||
#
|
||||
# @!macro executor_and_deref_options
|
||||
#
|
||||
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
||||
# block on execution
|
||||
#
|
||||
# @raise [ArgumentError] if no block is given
|
||||
#
|
||||
# @return [Future] the newly created `Future` in the `:pending` state
|
||||
#
|
||||
# @example
|
||||
# future = Concurrent::Future.execute{ sleep(1); 42 }
|
||||
# future.state #=> :pending
|
||||
def self.execute(opts = {}, &block)
|
||||
Future.new(opts, &block).execute
|
||||
end
|
||||
|
||||
# @!macro ivar_set_method
|
||||
def set(value = NULL, &block)
|
||||
check_for_block_or_value!(block_given?, value)
|
||||
synchronize do
|
||||
if @state != :unscheduled
|
||||
raise MultipleAssignmentError
|
||||
else
|
||||
@task = block || Proc.new { value }
|
||||
end
|
||||
end
|
||||
execute
|
||||
end
|
||||
|
||||
# Attempt to cancel the operation if it has not already processed.
|
||||
# The operation can only be cancelled while still `pending`. It cannot
|
||||
# be cancelled once it has begun processing or has completed.
|
||||
#
|
||||
# @return [Boolean] was the operation successfully cancelled.
|
||||
def cancel
|
||||
if compare_and_set_state(:cancelled, :pending)
|
||||
complete(false, nil, CancelledOperationError.new)
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Has the operation been successfully cancelled?
|
||||
#
|
||||
# @return [Boolean]
|
||||
def cancelled?
|
||||
state == :cancelled
|
||||
end
|
||||
|
||||
# Wait the given number of seconds for the operation to complete.
|
||||
# On timeout attempt to cancel the operation.
|
||||
#
|
||||
# @param [Numeric] timeout the maximum time in seconds to wait.
|
||||
# @return [Boolean] true if the operation completed before the timeout
|
||||
# else false
|
||||
def wait_or_cancel(timeout)
|
||||
wait(timeout)
|
||||
if complete?
|
||||
true
|
||||
else
|
||||
cancel
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def ns_initialize(value, opts)
|
||||
super
|
||||
@state = :unscheduled
|
||||
@task = opts[:__task_from_block__]
|
||||
@executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
|
||||
@args = get_arguments_from(opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user