concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -1,39 +1,42 @@
1
- require 'concurrent/dereferenceable'
2
- require 'concurrent/atomic/condition'
3
- require 'concurrent/atomic/event'
1
+ require 'concurrent/concern/dereferenceable'
4
2
 
5
3
  module Concurrent
6
4
 
7
- # An `MVar` is a synchronized single element container. They are empty or contain one item.
8
- # Taking a value from an empty `MVar` blocks, as does putting a value into a full one.
9
- # You can either think of them as blocking queue of length one, or a special kind of
10
- # mutable variable.
11
- #
12
- # On top of the fundamental `#put` and `#take` operations, we also provide a `#mutate`
13
- # that is atomic with respect to operations on the same instance. These operations all
14
- # support timeouts.
15
- #
16
- # We also support non-blocking operations `#try_put!` and `#try_take!`, a `#set!` that
17
- # ignores existing values, a `#value` that returns the value without removing it or
18
- # returns `MVar::EMPTY`, and a `#modify!` that yields `MVar::EMPTY` if the `MVar` is
19
- # empty and can be used to set `MVar::EMPTY`. You shouldn't use these operations in the
20
- # first instance.
21
- #
5
+ # An `MVar` is a synchronized single element container. They are empty or
6
+ # contain one item. Taking a value from an empty `MVar` blocks, as does
7
+ # putting a value into a full one. You can either think of them as blocking
8
+ # queue of length one, or a special kind of mutable variable.
9
+ #
10
+ # On top of the fundamental `#put` and `#take` operations, we also provide a
11
+ # `#mutate` that is atomic with respect to operations on the same instance.
12
+ # These operations all support timeouts.
13
+ #
14
+ # We also support non-blocking operations `#try_put!` and `#try_take!`, a
15
+ # `#set!` that ignores existing values, a `#value` that returns the value
16
+ # without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields
17
+ # `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`.
18
+ # You shouldn't use these operations in the first instance.
19
+ #
22
20
  # `MVar` is a [Dereferenceable](Dereferenceable).
23
- #
21
+ #
24
22
  # `MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala.
25
23
  #
26
24
  # Note that unlike the original Haskell paper, our `#take` is blocking. This is how
27
25
  # Haskell and Scala do it today.
28
- #
29
- # **See Also:**
30
- #
31
- # 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non-
32
- # strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991.
33
- # 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794). In Proceedings of the 23rd Symposium on Principles of Programming Languages (PoPL), 1996.
26
+ #
27
+ # @!macro copy_options
28
+ #
29
+ # ## See Also
30
+ #
31
+ # 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th
32
+ # ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991.
33
+ #
34
+ # 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794).
35
+ # In Proceedings of the 23rd Symposium on Principles of Programming Languages
36
+ # (PoPL), 1996.
34
37
  class MVar
35
38
 
36
- include Dereferenceable
39
+ include Concern::Dereferenceable
37
40
 
38
41
  # Unique value that represents that an `MVar` was empty
39
42
  EMPTY = Object.new
@@ -45,20 +48,13 @@ module Concurrent
45
48
  # Create a new `MVar`, either empty or with an initial value.
46
49
  #
47
50
  # @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
51
+ #
52
+ # @!macro deref_options
57
53
  def initialize(value = EMPTY, opts = {})
58
54
  @value = value
59
55
  @mutex = Mutex.new
60
- @empty_condition = Condition.new
61
- @full_condition = Condition.new
56
+ @empty_condition = ConditionVariable.new
57
+ @full_condition = ConditionVariable.new
62
58
  set_deref_options(opts)
63
59
  end
64
60
 
@@ -184,7 +180,7 @@ module Concurrent
184
180
 
185
181
  # Returns if the `MVar` currently contains a value.
186
182
  def full?
187
- not empty?
183
+ !empty?
188
184
  end
189
185
 
190
186
  private
@@ -206,12 +202,17 @@ module Concurrent
206
202
  end
207
203
 
208
204
  def wait_while(condition, timeout)
209
- remaining = Condition::Result.new(timeout)
210
- while yield && remaining.can_wait?
211
- remaining = condition.wait(@mutex, remaining.remaining_time)
205
+ if timeout.nil?
206
+ while yield
207
+ condition.wait(@mutex)
208
+ end
209
+ else
210
+ stop = Concurrent.monotonic_time + timeout
211
+ while yield && timeout > 0.0
212
+ condition.wait(@mutex, timeout)
213
+ timeout = stop - Concurrent.monotonic_time
214
+ end
212
215
  end
213
216
  end
214
-
215
217
  end
216
-
217
218
  end
@@ -1,7 +1,7 @@
1
1
  require 'thread'
2
-
3
- require 'concurrent/obligation'
4
- require 'concurrent/options_parser'
2
+ require 'concurrent/errors'
3
+ require 'concurrent/ivar'
4
+ require 'concurrent/executor/executor'
5
5
 
6
6
  module Concurrent
7
7
 
@@ -9,217 +9,236 @@ module Concurrent
9
9
 
10
10
  # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
11
11
  # and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications.
12
- #
13
- # > A promise represents the eventual value returned from the single completion of an operation.
14
- #
15
- # Promises are similar to futures and share many of the same behaviours. Promises are far more
16
- # robust, however. Promises can be chained in a tree structure where each promise may have zero
17
- # or more children. Promises are chained using the `then` method. The result of a call to `then`
18
- # is always another promise. Promises are resolved asynchronously (with respect to the main thread)
19
- # but in a strict order: parents are guaranteed to be resolved before their children, children
20
- # before their younger siblings. The `then` method takes two parameters: an optional block to
21
- # be executed upon parent resolution and an optional callable to be executed upon parent failure.
22
- # The result of each promise is passed to each of its children upon resolution. When a promise
23
- # is rejected all its children will be summarily rejected and will receive the reason.
24
- #
25
- # Promises have four possible states: *unscheduled*, *pending*, *rejected*, and *fulfilled*. A
26
- # Promise created using `.new` will be *unscheduled*. It is scheduled by calling the `execute`
27
- # method. Upon execution the Promise and all its children will be set to *pending*. When a promise
28
- # is *pending* it will remain in that state until processing is complete. A completed Promise is
29
- # either *rejected*, indicating that an exception was thrown during processing, or *fulfilled*,
30
- # indicating it succeeded. If a Promise is *fulfilled* its `value` will be updated to reflect
31
- # the result of the operation. If *rejected* the `reason` will be updated with a reference to
32
- # the thrown exception. The predicate methods `unscheduled?`, `pending?`, `rejected?`, and
33
- # `fulfilled?` can be called at any time to obtain the state of the Promise, as can the `state`
34
- # method, which returns a symbol. A Promise created using `.execute` will be *pending*, a Promise
35
- # created using `.fulfill(value)` will be *fulfilled* with the given value and a Promise created
36
- # using `.reject(reason)` will be *rejected* with the given reason.
37
- #
38
- # Retrieving the value of a promise is done through the `value` (alias: `deref`) method. Obtaining
39
- # the value of a promise is a potentially blocking operation. When a promise is *rejected* a call
40
- # to `value` will return `nil` immediately. When a promise is *fulfilled* a call to `value` will
41
- # immediately return the current value. When a promise is *pending* a call to `value` will block
42
- # until the promise is either *rejected* or *fulfilled*. A *timeout* value can be passed to `value`
43
- # to limit how long the call will block. If `nil` the call will block indefinitely. If `0` the call
44
- # will not block. Any other integer or float value will indicate the maximum number of seconds to block.
45
- #
12
+ #
13
+ # > A promise represents the eventual value returned from the single
14
+ # > completion of an operation.
15
+ #
16
+ # Promises are similar to futures and share many of the same behaviours.
17
+ # Promises are far more robust, however. Promises can be chained in a tree
18
+ # structure where each promise may have zero or more children. Promises are
19
+ # chained using the `then` method. The result of a call to `then` is always
20
+ # another promise. Promises are resolved asynchronously (with respect to the
21
+ # main thread) but in a strict order: parents are guaranteed to be resolved
22
+ # before their children, children before their younger siblings. The `then`
23
+ # method takes two parameters: an optional block to be executed upon parent
24
+ # resolution and an optional callable to be executed upon parent failure. The
25
+ # result of each promise is passed to each of its children upon resolution.
26
+ # When a promise is rejected all its children will be summarily rejected and
27
+ # will receive the reason.
28
+ #
29
+ # Promises have four possible states: *unscheduled*, *pending*, *rejected*,
30
+ # and *fulfilled*. A Promise created using `.new` will be *unscheduled*. It is
31
+ # scheduled by calling the `execute` method. Upon execution the Promise and
32
+ # all its children will be set to *pending*. When a promise is *pending* it
33
+ # will remain in that state until processing is complete. A completed Promise
34
+ # is either *rejected*, indicating that an exception was thrown during
35
+ # processing, or *fulfilled*, indicating it succeeded. If a Promise is
36
+ # *fulfilled* its `value` will be updated to reflect the result of the
37
+ # operation. If *rejected* the `reason` will be updated with a reference to
38
+ # the thrown exception. The predicate methods `unscheduled?`, `pending?`,
39
+ # `rejected?`, and `fulfilled?` can be called at any time to obtain the state
40
+ # of the Promise, as can the `state` method, which returns a symbol. A Promise
41
+ # created using `.execute` will be *pending*, a Promise created using
42
+ # `.fulfill(value)` will be *fulfilled* with the given value and a Promise
43
+ # created using `.reject(reason)` will be *rejected* with the given reason.
44
+ #
45
+ # Retrieving the value of a promise is done through the `value` (alias:
46
+ # `deref`) method. Obtaining the value of a promise is a potentially blocking
47
+ # operation. When a promise is *rejected* a call to `value` will return `nil`
48
+ # immediately. When a promise is *fulfilled* a call to `value` will
49
+ # immediately return the current value. When a promise is *pending* a call to
50
+ # `value` will block until the promise is either *rejected* or *fulfilled*. A
51
+ # *timeout* value can be passed to `value` to limit how long the call will
52
+ # block. If `nil` the call will block indefinitely. If `0` the call will not
53
+ # block. Any other integer or float value will indicate the maximum number of
54
+ # seconds to block.
55
+ #
46
56
  # Promises run on the global thread pool.
47
- #
57
+ #
58
+ # @!macro copy_options
59
+ #
48
60
  # ### Examples
49
- #
61
+ #
50
62
  # Start by requiring promises
51
- #
63
+ #
52
64
  # ```ruby
53
65
  # require 'concurrent'
54
66
  # ```
55
- #
67
+ #
56
68
  # Then create one
57
- #
69
+ #
58
70
  # ```ruby
59
71
  # p = Concurrent::Promise.execute do
60
72
  # # do something
61
73
  # 42
62
74
  # end
63
75
  # ```
64
- #
65
- # Promises can be chained using the `then` method. The `then` method accepts a block, to be executed
66
- # on fulfillment, and a callable argument to be executed on rejection. The result of the each promise
67
- # is passed as the block argument to chained promises.
68
- #
76
+ #
77
+ # Promises can be chained using the `then` method. The `then` method accepts a
78
+ # block, to be executed on fulfillment, and a callable argument to be executed
79
+ # on rejection. The result of the each promise is passed as the block argument
80
+ # to chained promises.
81
+ #
69
82
  # ```ruby
70
83
  # p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute
71
84
  # ```
72
- #
85
+ #
73
86
  # And so on, and so on, and so on...
74
- #
87
+ #
75
88
  # ```ruby
76
89
  # p = Concurrent::Promise.fulfill(20).
77
90
  # then{|result| result - 10 }.
78
91
  # then{|result| result * 3 }.
79
92
  # then{|result| result % 5 }.execute
80
93
  # ```
81
- #
94
+ #
82
95
  # The initial state of a newly created Promise depends on the state of its parent:
83
96
  # - if parent is *unscheduled* the child will be *unscheduled*
84
97
  # - if parent is *pending* the child will be *pending*
85
98
  # - if parent is *fulfilled* the child will be *pending*
86
99
  # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*)
87
- #
88
- # Promises are executed asynchronously from the main thread. By the time a child Promise finishes
89
- # nitialization it may be in a different state that its parent (by the time a child is created its parent
90
- # may have completed execution and changed state). Despite being asynchronous, however, the order of
91
- # execution of Promise objects in a chain (or tree) is strictly defined.
92
- #
93
- # There are multiple ways to create and execute a new `Promise`. Both ways provide identical behavior:
94
- #
100
+ #
101
+ # Promises are executed asynchronously from the main thread. By the time a
102
+ # child Promise finishes nitialization it may be in a different state that its
103
+ # parent (by the time a child is created its parent may have completed
104
+ # execution and changed state). Despite being asynchronous, however, the order
105
+ # of execution of Promise objects in a chain (or tree) is strictly defined.
106
+ #
107
+ # There are multiple ways to create and execute a new `Promise`. Both ways
108
+ # provide identical behavior:
109
+ #
95
110
  # ```ruby
96
111
  # # create, operate, then execute
97
112
  # p1 = Concurrent::Promise.new{ "Hello World!" }
98
113
  # p1.state #=> :unscheduled
99
114
  # p1.execute
100
- #
115
+ #
101
116
  # # create and immediately execute
102
117
  # p2 = Concurrent::Promise.new{ "Hello World!" }.execute
103
- #
118
+ #
104
119
  # # execute during creation
105
120
  # p3 = Concurrent::Promise.execute{ "Hello World!" }
106
121
  # ```
107
- #
122
+ #
108
123
  # Once the `execute` method is called a `Promise` becomes `pending`:
109
- #
124
+ #
110
125
  # ```ruby
111
126
  # p = Concurrent::Promise.execute{ "Hello, world!" }
112
127
  # p.state #=> :pending
113
128
  # p.pending? #=> true
114
129
  # ```
115
- #
130
+ #
116
131
  # Wait a little bit, and the promise will resolve and provide a value:
117
- #
132
+ #
118
133
  # ```ruby
119
134
  # p = Concurrent::Promise.execute{ "Hello, world!" }
120
135
  # sleep(0.1)
121
- #
136
+ #
122
137
  # p.state #=> :fulfilled
123
138
  # p.fulfilled? #=> true
124
139
  # p.value #=> "Hello, world!"
125
140
  # ```
126
- #
141
+ #
127
142
  # If an exception occurs, the promise will be rejected and will provide
128
143
  # a reason for the rejection:
129
- #
144
+ #
130
145
  # ```ruby
131
146
  # p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") }
132
147
  # sleep(0.1)
133
- #
148
+ #
134
149
  # p.state #=> :rejected
135
150
  # p.rejected? #=> true
136
151
  # p.reason #=> "#<StandardError: Here comes the Boom!>"
137
152
  # ```
138
- #
153
+ #
139
154
  # #### Rejection
140
- #
141
- # When a promise is rejected all its children will be rejected and will receive the rejection `reason`
142
- # as the rejection callable parameter:
143
- #
155
+ #
156
+ # When a promise is rejected all its children will be rejected and will
157
+ # receive the rejection `reason` as the rejection callable parameter:
158
+ #
144
159
  # ```ruby
145
160
  # p = [ Concurrent::Promise.execute{ Thread.pass; raise StandardError } ]
146
- #
161
+ #
147
162
  # c1 = p.then(Proc.new{ |reason| 42 })
148
163
  # c2 = p.then(Proc.new{ |reason| raise 'Boom!' })
149
- #
164
+ #
150
165
  # sleep(0.1)
151
- #
166
+ #
152
167
  # c1.state #=> :rejected
153
168
  # c2.state #=> :rejected
154
169
  # ```
155
- #
156
- # Once a promise is rejected it will continue to accept children that will receive immediately rejection
157
- # (they will be executed asynchronously).
158
- #
170
+ #
171
+ # Once a promise is rejected it will continue to accept children that will
172
+ # receive immediately rejection (they will be executed asynchronously).
173
+ #
159
174
  # #### Aliases
160
- #
161
- # The `then` method is the most generic alias: it accepts a block to be executed upon parent fulfillment
162
- # and a callable to be executed upon parent rejection. At least one of them should be passed. The default
163
- # block is `{ |result| result }` that fulfills the child with the parent value. The default callable is
164
- # `{ |reason| raise reason }` that rejects the child with the parent reason.
165
- #
175
+ #
176
+ # The `then` method is the most generic alias: it accepts a block to be
177
+ # executed upon parent fulfillment and a callable to be executed upon parent
178
+ # rejection. At least one of them should be passed. The default block is `{
179
+ # |result| result }` that fulfills the child with the parent value. The
180
+ # default callable is `{ |reason| raise reason }` that rejects the child with
181
+ # the parent reason.
182
+ #
166
183
  # - `on_success { |result| ... }` is the same as `then {|result| ... }`
167
184
  # - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )`
168
185
  # - `rescue` is aliased by `catch` and `on_error`
169
- class Promise
170
- # TODO unify promise and future to single class, with dataflow
171
- include Obligation
186
+ class Promise < IVar
172
187
 
173
188
  # Initialize a new Promise with the provided options.
174
189
  #
175
- # @param [Hash] opts the options used to define the behavior at update and deref
190
+ # @!macro executor_and_deref_options
191
+ #
192
+ # @!macro [attach] promise_init_options
176
193
  #
177
- # @option opts [Promise] :parent the parent `Promise` when building a chain/tree
178
- # @option opts [Proc] :on_fulfill fulfillment handler
179
- # @option opts [Proc] :on_reject rejection handler
194
+ # @option opts [Promise] :parent the parent `Promise` when building a chain/tree
195
+ # @option opts [Proc] :on_fulfill fulfillment handler
196
+ # @option opts [Proc] :on_reject rejection handler
197
+ # @option opts [object, Array] :args zero or more arguments to be passed
198
+ # the task block on execution
180
199
  #
181
- # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
182
- # operation pool (for long-running operations), when `false` will execute the future on the
183
- # global task pool (for short-running tasks)
184
- # @option opts [object] :executor when provided will run all operations on
185
- # this executor rather than the global thread pool (overrides :operation)
200
+ # @yield The block operation to be performed asynchronously.
186
201
  #
187
- # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
188
- # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
189
- # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
190
- # returning the value returned from the proc
202
+ # @raise [ArgumentError] if no block is given
191
203
  #
192
204
  # @see http://wiki.commonjs.org/wiki/Promises/A
193
205
  # @see http://promises-aplus.github.io/promises-spec/
194
206
  def initialize(opts = {}, &block)
195
207
  opts.delete_if { |k, v| v.nil? }
196
-
197
- @executor = OptionsParser::get_executor_from(opts) || Concurrent.configuration.global_operation_pool
198
- @parent = opts.fetch(:parent) { nil }
199
- @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } }
200
- @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } }
201
-
202
- @promise_body = block || Proc.new { |result| result }
203
- @state = :unscheduled
204
- @children = []
205
-
206
- init_obligation
207
- set_deref_options(opts)
208
+ super(IVar::NO_VALUE, opts.merge(__promise_body_from_block__: block), &nil)
208
209
  end
209
210
 
210
- # @return [Promise]
211
+ # Create a new `Promise` and fulfill it immediately.
212
+ #
213
+ # @!macro executor_and_deref_options
214
+ #
215
+ # @!macro promise_init_options
216
+ #
217
+ # @raise [ArgumentError] if no block is given
218
+ #
219
+ # @return [Promise] the newly created `Promise`
211
220
  def self.fulfill(value, opts = {})
212
221
  Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) }
213
222
  end
214
223
 
215
-
216
- # @return [Promise]
224
+ # Create a new `Promise` and reject it immediately.
225
+ #
226
+ # @!macro executor_and_deref_options
227
+ #
228
+ # @!macro promise_init_options
229
+ #
230
+ # @raise [ArgumentError] if no block is given
231
+ #
232
+ # @return [Promise] the newly created `Promise`
217
233
  def self.reject(reason, opts = {})
218
234
  Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) }
219
235
  end
220
236
 
221
- # @return [Promise]
222
- # @since 0.5.0
237
+ # Execute an `:unscheduled` `Promise`. Immediately sets the state to `:pending` and
238
+ # passes the block to a new thread/thread pool for eventual execution.
239
+ # Does nothing if the `Promise` is in any state other than `:unscheduled`.
240
+ #
241
+ # @return [Promise] a reference to `self`
223
242
  def execute
224
243
  if root?
225
244
  if compare_and_set_state(:pending, :unscheduled)
@@ -232,11 +251,54 @@ module Concurrent
232
251
  self
233
252
  end
234
253
 
235
- # @since 0.5.0
254
+ # @!macro ivar_set_method
255
+ #
256
+ # @raise [Concurrent::PromiseExecutionError] if not the root promise
257
+ def set(value = IVar::NO_VALUE, &block)
258
+ raise PromiseExecutionError.new('supported only on root promise') unless root?
259
+ check_for_block_or_value!(block_given?, value)
260
+ synchronize do
261
+ if @state != :unscheduled
262
+ raise MultipleAssignmentError
263
+ else
264
+ @promise_body = block || Proc.new { |result| value }
265
+ end
266
+ end
267
+ execute
268
+ end
269
+
270
+ # @!macro ivar_fail_method
271
+ #
272
+ # @raise [Concurrent::PromiseExecutionError] if not the root promise
273
+ def fail(reason = StandardError.new)
274
+ set { raise reason }
275
+ end
276
+
277
+ # Create a new `Promise` object with the given block, execute it, and return the
278
+ # `:pending` object.
279
+ #
280
+ # @!macro executor_and_deref_options
281
+ #
282
+ # @!macro promise_init_options
283
+ #
284
+ # @return [Promise] the newly created `Promise` in the `:pending` state
285
+ #
286
+ # @raise [ArgumentError] if no block is given
287
+ #
288
+ # @example
289
+ # promise = Concurrent::Promise.execute{ sleep(1); 42 }
290
+ # promise.state #=> :pending
236
291
  def self.execute(opts = {}, &block)
237
292
  new(opts, &block).execute
238
293
  end
239
294
 
295
+ # Chain a new promise off the current promise.
296
+ #
297
+ # @param [Proc] rescuer An optional rescue block to be executed if the
298
+ # promise is rejected.
299
+ #
300
+ # @yield The block operation to be performed asynchronously.
301
+ #
240
302
  # @return [Promise] the new promise
241
303
  def then(rescuer = nil, &block)
242
304
  raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
@@ -248,7 +310,7 @@ module Concurrent
248
310
  on_reject: rescuer
249
311
  )
250
312
 
251
- mutex.synchronize do
313
+ synchronize do
252
314
  child.state = :pending if @state == :pending
253
315
  child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled
254
316
  child.on_reject(@reason) if @state == :rejected
@@ -258,13 +320,23 @@ module Concurrent
258
320
  child
259
321
  end
260
322
 
261
- # @return [Promise]
323
+ # Chain onto this promise an action to be undertaken on success
324
+ # (fulfillment).
325
+ #
326
+ # @yield The block to execute
327
+ #
328
+ # @return [Promise] self
262
329
  def on_success(&block)
263
330
  raise ArgumentError.new('no block given') unless block_given?
264
331
  self.then(&block)
265
332
  end
266
333
 
267
- # @return [Promise]
334
+ # Chain onto this promise an action to be undertaken on failure
335
+ # (rejection).
336
+ #
337
+ # @yield The block to execute
338
+ #
339
+ # @return [Promise] self
268
340
  def rescue(&block)
269
341
  self.then(block)
270
342
  end
@@ -366,6 +438,21 @@ module Concurrent
366
438
 
367
439
  protected
368
440
 
441
+ def ns_initialize(value, opts)
442
+ super
443
+
444
+ @executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
445
+ @args = get_arguments_from(opts)
446
+
447
+ @parent = opts.fetch(:parent) { nil }
448
+ @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } }
449
+ @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } }
450
+
451
+ @promise_body = opts[:__promise_body_from_block__] || Proc.new { |result| result }
452
+ @state = :unscheduled
453
+ @children = []
454
+ end
455
+
369
456
  # Aggregate a collection of zero or more promises under a composite promise,
370
457
  # execute the aggregated promises and collect them into a standard Ruby array,
371
458
  # call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`,
@@ -389,8 +476,9 @@ module Concurrent
389
476
  composite
390
477
  end
391
478
 
479
+ # @!visibility private
392
480
  def set_pending
393
- mutex.synchronize do
481
+ synchronize do
394
482
  @state = :pending
395
483
  @children.each { |c| c.set_pending }
396
484
  end
@@ -413,35 +501,40 @@ module Concurrent
413
501
  nil
414
502
  end
415
503
 
504
+ # @!visibility private
416
505
  def notify_child(child)
417
506
  if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) }
418
507
  if_state(:rejected) { child.on_reject(@reason) }
419
508
  end
420
509
 
421
510
  # @!visibility private
422
- def realize(task)
423
- @executor.post do
424
- success, value, reason = SafeTaskExecutor.new(task).execute
511
+ def complete(success, value, reason)
512
+ children_to_notify = synchronize do
513
+ set_state!(success, value, reason)
514
+ @children.dup
515
+ end
425
516
 
426
- children_to_notify = mutex.synchronize do
427
- set_state!(success, value, reason)
428
- @children.dup
429
- end
517
+ children_to_notify.each { |child| notify_child(child) }
518
+ observers.notify_and_delete_observers{ [Time.now, self.value, reason] }
519
+ end
430
520
 
431
- children_to_notify.each { |child| notify_child(child) }
521
+ # @!visibility private
522
+ def realize(task)
523
+ @executor.post do
524
+ success, value, reason = SafeTaskExecutor.new(task).execute(*@args)
525
+ complete(success, value, reason)
432
526
  end
433
527
  end
434
528
 
529
+ # @!visibility private
435
530
  def set_state!(success, value, reason)
436
531
  set_state(success, value, reason)
437
532
  event.set
438
533
  end
439
534
 
535
+ # @!visibility private
440
536
  def synchronized_set_state!(success, value, reason)
441
- mutex.lock
442
- set_state!(success, value, reason)
443
- ensure
444
- mutex.unlock
537
+ synchronize { set_state!(success, value, reason) }
445
538
  end
446
539
  end
447
540
  end