• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jdantonio / concurrent-ruby / #812

23 May 2014 08:08PM UTC coverage: 39.344% (-57.9%) from 97.237%
#812

push

jdantonio
Merge pull request #96 from jdantonio/refactor/errors

Moved all custom errors into a single file and into the Concurrent module

17 of 18 new or added lines in 10 files covered. (94.44%)

1435 existing lines in 57 files now uncovered.

1187 of 3017 relevant lines covered (39.34%)

0.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.0
/lib/concurrent/future.rb
1
require 'thread'
1✔
2

3
require 'concurrent/options_parser'
1✔
4
require 'concurrent/executor/safe_task_executor'
1✔
5

6
module Concurrent
1✔
7

8
  # A `Future` represents a promise to complete an action at some time in the future.
9
  # The action is atomic and permanent. The idea behind a future is to send an operation
10
  # for asynchronous completion, do other stuff, then return and retrieve the result
11
  # of the async operation at a later time.
12
  #
13
  # A `Future` has four possible states: *:unscheduled*, *:pending*, *:rejected*, or *:fulfilled*.
14
  # When a `Future` is created its state is set to *:unscheduled*. Once the `#execute` method is
15
  # called the state becomes *:pending* and will remain in that state until processing is
16
  # complete. A completed `Future` is either *:rejected*, indicating that an exception was
17
  # thrown during processing, or *:fulfilled*, indicating success. If a `Future` is *:fulfilled*
18
  # its `value` will be updated to reflect the result of the operation. If *:rejected* the
19
  # `reason` will be updated with a reference to the thrown exception. The predicate methods
20
  # `#unscheduled?`, `#pending?`, `#rejected?`, and `fulfilled?` can be called at any time to
21
  # obtain the state of the `Future`, as can the `#state` method, which returns a symbol. 
22
  #
23
  # Retrieving the value of a `Future` is done through the `#value` (alias: `#deref`) method.
24
  # Obtaining the value of a `Future` is a potentially blocking operation. When a `Future` is
25
  # *:rejected* a call to `#value` will return `nil` immediately. When a `Future` is
26
  # *:fulfilled* a call to `#value` will immediately return the current value. When a
27
  # `Future` is *:pending* a call to `#value` will block until the `Future` is either
28
  # *:rejected* or *:fulfilled*. A *timeout* value can be passed to `#value` to limit how
29
  # long the call will block. If `nil` the call will block indefinitely. If `0` the call will
30
  # not block. Any other integer or float value will indicate the maximum number of seconds to block.
31
  #
32
  # The `Future` class also includes the behavior of the Ruby standard library `Observable` module,
33
  # but does so in a thread-safe way. On fulfillment or rejection all observers will be notified
34
  # according to the normal `Observable` behavior. The observer callback function will be called
35
  # with three parameters: the `Time` of fulfillment/rejection, the final `value`, and the final
36
  # `reason`. Observers added after fulfillment/rejection will still be notified as normal.
37
  #
38
  # @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module
39
  # @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function
40
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
41
  class Future < IVar
1✔
42

43
    # Create a new `Future` in the `:unscheduled` state.
44
    #
45
    # @yield the asynchronous operation to perform
46
    #
47
    # @param [Hash] opts the options controlling how the future will be processed
48
    # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
49
    #   operation pool (for long-running operations), when `false` will execute the future on the
50
    #   global task pool (for short-running tasks)
51
    # @option opts [object] :executor when provided will run all operations on
52
    #   this executor rather than the global thread pool (overrides :operation)
53
    # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
54
    # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
55
    # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
56
    #   returning the value returned from the proc
57
    #
58
    # @raise [ArgumentError] if no block is given
59
    def initialize(opts = {}, &block)
1✔
UNCOV
60
      raise ArgumentError.new('no block given') unless block_given?
×
UNCOV
61
      super(IVar::NO_VALUE, opts)
×
UNCOV
62
      @state = :unscheduled
×
UNCOV
63
      @task = block
×
UNCOV
64
      @executor = OptionsParser::get_executor_from(opts)
×
65
    end
66

67
    # Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and
68
    # passes the block to a new thread/thread pool for eventual execution.
69
    # Does nothing if the `Future` is in any state other than `:unscheduled`.
70
    #
71
    # @return [Future] a reference to `self`
72
    #
73
    # @example Instance and execute in separate steps
74
    #   future = Concurrent::Future.new{ sleep(1); 42 }
75
    #   future.state #=> :unscheduled
76
    #   future.execute
77
    #   future.state #=> :pending
78
    #
79
    # @example Instance and execute in one line
80
    #   future = Concurrent::Future.new{ sleep(1); 42 }.execute
81
    #   future.state #=> :pending
82
    #
83
    # @since 0.5.0
84
    def execute
1✔
UNCOV
85
      if compare_and_set_state(:pending, :unscheduled)
×
UNCOV
86
        @executor.post{ work }
×
UNCOV
87
        self
×
88
      end
89
    end
90

91
    # Create a new `Future` object with the given block, execute it, and return the
92
    # `:pending` object.
93
    #
94
    # @yield the asynchronous operation to perform
95
    #
96
    # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
97
    # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
98
    # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
99
    #   returning the value returned from the proc
100
    #
101
    # @return [Future] the newly created `Future` in the `:pending` state
102
    #
103
    # @raise [ArgumentError] if no block is given
104
    #
105
    # @example
106
    #   future = Concurrent::Future.execute{ sleep(1); 42 }
107
    #   future.state #=> :pending
108
    #
109
    # @since 0.5.0
110
    def self.execute(opts = {}, &block)
1✔
UNCOV
111
      Future.new(opts, &block).execute
×
112
    end
113

114
    protected :set, :fail, :complete
1✔
115

116
    private
1✔
117

118
    # @!visibility private
119
    def work # :nodoc:
1✔
UNCOV
120
      success, val, reason = SafeTaskExecutor.new(@task).execute
×
UNCOV
121
      complete(success, val, reason)
×
122
    end
123
  end
124
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc