concurrent-ruby 0.4.1 → 0.5.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -33
  3. data/lib/concurrent.rb +11 -3
  4. data/lib/concurrent/actor.rb +29 -29
  5. data/lib/concurrent/agent.rb +98 -16
  6. data/lib/concurrent/atomic.rb +125 -0
  7. data/lib/concurrent/channel.rb +36 -1
  8. data/lib/concurrent/condition.rb +67 -0
  9. data/lib/concurrent/copy_on_notify_observer_set.rb +80 -0
  10. data/lib/concurrent/copy_on_write_observer_set.rb +94 -0
  11. data/lib/concurrent/count_down_latch.rb +60 -0
  12. data/lib/concurrent/dataflow.rb +85 -0
  13. data/lib/concurrent/dereferenceable.rb +69 -31
  14. data/lib/concurrent/event.rb +27 -21
  15. data/lib/concurrent/future.rb +103 -43
  16. data/lib/concurrent/ivar.rb +78 -0
  17. data/lib/concurrent/mvar.rb +154 -0
  18. data/lib/concurrent/obligation.rb +94 -9
  19. data/lib/concurrent/postable.rb +11 -9
  20. data/lib/concurrent/promise.rb +101 -127
  21. data/lib/concurrent/safe_task_executor.rb +28 -0
  22. data/lib/concurrent/scheduled_task.rb +60 -54
  23. data/lib/concurrent/stoppable.rb +2 -2
  24. data/lib/concurrent/supervisor.rb +36 -29
  25. data/lib/concurrent/thread_local_var.rb +117 -0
  26. data/lib/concurrent/timer_task.rb +28 -30
  27. data/lib/concurrent/utilities.rb +1 -1
  28. data/lib/concurrent/version.rb +1 -1
  29. data/spec/concurrent/agent_spec.rb +121 -230
  30. data/spec/concurrent/atomic_spec.rb +201 -0
  31. data/spec/concurrent/condition_spec.rb +171 -0
  32. data/spec/concurrent/copy_on_notify_observer_set_spec.rb +10 -0
  33. data/spec/concurrent/copy_on_write_observer_set_spec.rb +10 -0
  34. data/spec/concurrent/count_down_latch_spec.rb +125 -0
  35. data/spec/concurrent/dataflow_spec.rb +160 -0
  36. data/spec/concurrent/dereferenceable_shared.rb +145 -0
  37. data/spec/concurrent/event_spec.rb +44 -9
  38. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -1
  39. data/spec/concurrent/future_spec.rb +184 -69
  40. data/spec/concurrent/ivar_spec.rb +192 -0
  41. data/spec/concurrent/mvar_spec.rb +380 -0
  42. data/spec/concurrent/obligation_spec.rb +193 -0
  43. data/spec/concurrent/observer_set_shared.rb +233 -0
  44. data/spec/concurrent/postable_shared.rb +3 -7
  45. data/spec/concurrent/promise_spec.rb +270 -192
  46. data/spec/concurrent/safe_task_executor_spec.rb +58 -0
  47. data/spec/concurrent/scheduled_task_spec.rb +142 -38
  48. data/spec/concurrent/thread_local_var_spec.rb +113 -0
  49. data/spec/concurrent/thread_pool_shared.rb +2 -3
  50. data/spec/concurrent/timer_task_spec.rb +31 -1
  51. data/spec/spec_helper.rb +2 -3
  52. data/spec/support/functions.rb +4 -0
  53. data/spec/support/less_than_or_equal_to_matcher.rb +5 -0
  54. metadata +50 -30
  55. data/lib/concurrent/contract.rb +0 -21
  56. data/lib/concurrent/event_machine_defer_proxy.rb +0 -22
  57. data/md/actor.md +0 -404
  58. data/md/agent.md +0 -142
  59. data/md/channel.md +0 -40
  60. data/md/dereferenceable.md +0 -49
  61. data/md/future.md +0 -125
  62. data/md/obligation.md +0 -32
  63. data/md/promise.md +0 -217
  64. data/md/scheduled_task.md +0 -156
  65. data/md/supervisor.md +0 -246
  66. data/md/thread_pool.md +0 -225
  67. data/md/timer_task.md +0 -191
  68. data/spec/concurrent/contract_spec.rb +0 -34
  69. data/spec/concurrent/event_machine_defer_proxy_spec.rb +0 -240
@@ -1,142 +0,0 @@
1
- # Secret Agent Man
2
-
3
- `Agent`s are inspired by [Clojure's](http://clojure.org/) [agent](http://clojure.org/agents) function.
4
- An `Agent` is a single atomic value that represents an identity. The current value
5
- of the `Agent` can be requested at any time (`deref`). Each `Agent` has a work queue and operates on
6
- the global thread pool (see below). Consumers can `post` code blocks to the
7
- `Agent`. The code block (function) will receive the current value of the `Agent` as its sole
8
- parameter. The return value of the block will become the new value of the `Agent`. `Agent`s support
9
- two error handling modes: fail and continue. A good example of an `Agent` is a shared incrementing
10
- counter, such as the score in a video game.
11
-
12
- An `Agent` must be initialize with an initial value. This value is always accessible via the `value`
13
- (or `deref`) methods. Code blocks sent to the `Agent` will be processed in the order received. As
14
- each block is processed the current value is updated with the result from the block. This update
15
- is an atomic operation so a `deref` will never block and will always return the current value.
16
-
17
- When an `Agent` is created it may be given an optional `validate` block and zero or more `rescue`
18
- blocks. When a new value is calculated the value will be checked against the validator, if present.
19
- If the validator returns `true` the new value will be accepted. If it returns `false` it will be
20
- rejected. If a block raises an exception during execution the list of `rescue` blocks will be
21
- seacrhed in order until one matching the current exception is found. That `rescue` block will
22
- then be called an passed the exception object. If no matching `rescue` block is found, or none
23
- were configured, then the exception will be suppressed.
24
-
25
- `Agent`s also implement Ruby's [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html).
26
- Code that observes an `Agent` will receive a callback with the new value any time the value
27
- is changed.
28
-
29
- ## Copy Options
30
-
31
- Object references in Ruby are mutable. This can lead to serious problems when
32
- the value of an `Agent` is a mutable reference. Which is always the case unless
33
- the value is a `Fixnum`, `Symbol`, or similar "primative" data type. Each
34
- `Agent` instance can be configured with a few options that can help protect the
35
- program from potentially dangerous operations. Each of these options can be
36
- optionally set when the `Agent` is created:
37
-
38
- * `:dup_on_deref` when true the `Agent` will call the `#dup` method on the
39
- `value` object every time the `#value` methid is called (default: false)
40
- * `:freeze_on_deref` when true the `Agent` will call the `#freeze` method on the
41
- `value` object every time the `#value` method is called (default: false)
42
- * `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time
43
- the `#value` method is called. The `Proc` will be given the current `value` as
44
- its only parameter and the result returned by the block will be the return
45
- value of the `#value` call. When `nil` this option will be ignored (default:
46
- nil)
47
-
48
- ## Examples
49
-
50
- A simple example:
51
-
52
- ```ruby
53
- require 'concurrent'
54
-
55
- score = Concurrent::Agent.new(10)
56
- score.value #=> 10
57
-
58
- score << proc{|current| current + 100 }
59
- sleep(0.1)
60
- score.value #=> 110
61
-
62
- score << proc{|current| current * 2 }
63
- sleep(0.1)
64
- score.value #=> 220
65
-
66
- score << proc{|current| current - 50 }
67
- sleep(0.1)
68
- score.value #=> 170
69
- ```
70
-
71
- With validation and error handling:
72
-
73
- ```ruby
74
- score = Concurrent::Agent.new(0).validate{|value| value <= 1024 }.
75
- rescue(NoMethodError){|ex| puts "Bam!" }.
76
- rescue(ArgumentError){|ex| puts "Pow!" }.
77
- rescue{|ex| puts "Boom!" }
78
- score.value #=> 0
79
-
80
- score << proc{|current| current + 2048 }
81
- sleep(0.1)
82
- score.value #=> 0
83
-
84
- score << proc{|current| raise ArgumentError }
85
- sleep(0.1)
86
- #=> puts "Pow!"
87
- score.value #=> 0
88
-
89
- score << proc{|current| current + 100 }
90
- sleep(0.1)
91
- score.value #=> 100
92
- ```
93
-
94
- With observation:
95
-
96
- ```ruby
97
- bingo = Class.new{
98
- def update(time, score)
99
- puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
100
- end
101
- }.new
102
-
103
- score = Concurrent::Agent.new(0)
104
- score.add_observer(bingo)
105
-
106
- score << proc{|current| sleep(0.1); current += 30 }
107
- score << proc{|current| sleep(0.1); current += 30 }
108
- score << proc{|current| sleep(0.1); current += 30 }
109
- score << proc{|current| sleep(0.1); current += 30 }
110
-
111
- sleep(1)
112
- #=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
113
- ```
114
-
115
- ## Copyright
116
-
117
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
118
- It is free software and may be redistributed under the terms specified in the LICENSE file.
119
-
120
- ## License
121
-
122
- Released under the MIT license.
123
-
124
- http://www.opensource.org/licenses/mit-license.php
125
-
126
- > Permission is hereby granted, free of charge, to any person obtaining a copy
127
- > of this software and associated documentation files (the "Software"), to deal
128
- > in the Software without restriction, including without limitation the rights
129
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
130
- > copies of the Software, and to permit persons to whom the Software is
131
- > furnished to do so, subject to the following conditions:
132
- >
133
- > The above copyright notice and this permission notice shall be included in
134
- > all copies or substantial portions of the Software.
135
- >
136
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
137
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
138
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
139
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
140
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
141
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
142
- > THE SOFTWARE.
@@ -1,40 +0,0 @@
1
- # Change the dang channel!
2
-
3
- `Channel` is a functional programming variation of `Actor`, based very loosely on the
4
- [MailboxProcessor](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx)
5
- agent in [F#](http://msdn.microsoft.com/en-us/library/ee370357.aspx).
6
- The `Actor` is used to create objects that receive messages from other
7
- threads then processes those messages based on the behavior of the class. `Channel`
8
- creates objects that receive messages and processe them using the block given
9
- at construction. `Channel` is implemented as a subclass of
10
- [Actor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/actor.md)
11
- and supports all message-passing methods of that class. `Channel` also supports pools
12
- with a shared mailbox.
13
-
14
- See the [Actor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/actor.md)
15
- documentation for more detail.
16
-
17
- ## Usage
18
-
19
- ```ruby
20
- require 'concurrent'
21
-
22
- channel = Concurrent::Channel.new do |msg|
23
- sleep(1)
24
- puts "#{msg}\n"
25
- end
26
-
27
- channel.run! => #<Thread:0x007fa123d95fc8 sleep>
28
-
29
- channel.post("Hello, World!") => 1
30
- # wait...
31
- => Hello, World!
32
-
33
- future = channel.post? "Don't Panic." => #<Concurrent::Contract:0x007fa123d6d9d8 @state=:pending...
34
- future.pending? => true
35
- # wait...
36
- => "Don't Panic."
37
- future.fulfilled? => true
38
-
39
- channel.stop => true
40
- ```
@@ -1,49 +0,0 @@
1
- # Dereferenceable
2
-
3
- Object references in Ruby are mutable. This can lead to serious problems when
4
- the `#value` of a concurrent object is a mutable reference. Which is always the
5
- case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type.
6
- Most classes in this library that expose a `#value` getter method do so using
7
- the `Dereferenceable` mixin module.
8
-
9
- Objects with this mixin can be configured with a few options that can help protect
10
- the program from potentially dangerous operations.
11
-
12
- * `:dup_on_deref` when true will call the `#dup` method on the
13
- `value` object every time the `#value` method is called (default: false)
14
- * `:freeze_on_deref` when true will call the `#freeze` method on the
15
- `value` object every time the `#value` method is called (default: false)
16
- * `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time
17
- the `#value` method is called. The `Proc` will be given the current `value` as
18
- its only parameter and the result returned by the block will be the return
19
- value of the `#value` call. When `nil` this option will be ignored (default:
20
- nil)
21
-
22
- ## Copyright
23
-
24
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
25
- It is free software and may be redistributed under the terms specified in the LICENSE file.
26
-
27
- ## License
28
-
29
- Released under the MIT license.
30
-
31
- http://www.opensource.org/licenses/mit-license.php
32
-
33
- > Permission is hereby granted, free of charge, to any person obtaining a copy
34
- > of this software and associated documentation files (the "Software"), to deal
35
- > in the Software without restriction, including without limitation the rights
36
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37
- > copies of the Software, and to permit persons to whom the Software is
38
- > furnished to do so, subject to the following conditions:
39
- >
40
- > The above copyright notice and this permission notice shall be included in
41
- > all copies or substantial portions of the Software.
42
- >
43
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49
- > THE SOFTWARE.
@@ -1,125 +0,0 @@
1
- # We're Sending You Back to the Future!
2
-
3
- `Future`s are inspired by [Clojure's](http://clojure.org/) [future](http://clojuredocs.org/clojure_core/clojure.core/future)
4
- function. A future represents a promise to complete an action at some time in the future.
5
- The action is atomic and permanent. The idea behind a future is to send an operation for
6
- asynchronous completion, do other stuff, then return and retrieve the result of the async
7
- operation at a later time. `Future`s run on the global thread pool (see below).
8
-
9
- `Future`s have three possible states: *pending*, *rejected*, and *fulfilled*. When a `Future` is created it is set
10
- to *pending* and will remain in that state until processing is complete. A completed `Future` is either *rejected*,
11
- indicating that an exception was thrown during processing, or *fulfilled*, indicating succedd. If a `Future` is
12
- *fulfilled* its `value` will be updated to reflect the result of the operation. If *rejected* the `reason` will
13
- be updated with a reference to the thrown exception. The predicate methods `pending?`, `rejected`, and `fulfilled?`
14
- can be called at any time to obtain the state of the `Future`, as can the `state` method, which returns a symbol.
15
-
16
- Retrieving the value of a `Future` is done through the `value` (alias: `deref`) method. Obtaining the value of
17
- a `Future` is a potentially blocking operation. When a `Future` is *rejected* a call to `value` will return `nil`
18
- immediately. When a `Future` is *fulfilled* a call to `value` will immediately return the current value.
19
- When a `Future` is *pending* a call to `value` will block until the `Future` is either *rejected* or *fulfilled*.
20
- A *timeout* value can be passed to `value` to limit how long the call will block. If `nil` the call will
21
- block indefinitely. If `0` the call will not block. Any other integer or float value will indicate the
22
- maximum number of seconds to block.
23
-
24
- The `Future` class also includes the Ruby standard library
25
- [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html) module. On fulfillment
26
- or rejection all observers will be notified according to the normal `Observable` behavior. The observer
27
- callback function will be called with three parameters: the `Time` of fulfillment/rejection, the
28
- final `value`, and the final `reason`. Observers added after fulfillment/rejection will still be
29
- notified as normal. The notification will occur on the global thread pool.
30
-
31
- ## Examples
32
-
33
- A fulfilled example:
34
-
35
- ```ruby
36
- require 'concurrent'
37
-
38
- count = Concurrent::Future.new{ sleep(10); 10 }
39
- count.state #=> :pending
40
- count.pending? #=> true
41
-
42
- # do stuff...
43
-
44
- count.value(0) #=> nil (does not block)
45
-
46
- count.value #=> 10 (after blocking)
47
- count.state #=> :fulfilled
48
- count.fulfilled? #=> true
49
- count.value #=> 10
50
- ```
51
-
52
- A rejected example:
53
-
54
- ```ruby
55
- count = Concurrent::Future.new{ sleep(10); raise StandardError.new("Boom!") }
56
- count.state #=> :pending
57
- count.pending? #=> true
58
-
59
- deref(count) #=> nil (after blocking)
60
- count.rejected? #=> true
61
- count.reason #=> #<StandardError: Boom!>
62
- ```
63
-
64
- An example with observation:
65
-
66
- ```ruby
67
- class Ticker
68
- Stock = Struct.new(:symbol, :name, :exchange)
69
-
70
- def update(time, value, reason)
71
- ticker = value.collect do |symbol|
72
- Stock.new(symbol['symbol'], symbol['name'], symbol['exch'])
73
- end
74
-
75
- output = ticker.join("\n")
76
- print "#{output}\n"
77
- end
78
- end
79
-
80
- yahoo = Finance.new('YAHOO')
81
- future = Concurrent::Future.new { yahoo.update.suggested_symbols }
82
- future.add_observer(Ticker.new)
83
-
84
- # do important stuff...
85
-
86
- #>> #<struct Ticker::Stock symbol="YHOO", name="Yahoo! Inc.", exchange="NMS">
87
- #>> #<struct Ticker::Stock symbol="YHO.DE", name="Yahoo! Inc.", exchange="GER">
88
- #>> #<struct Ticker::Stock symbol="YAHOY", name="Yahoo Japan Corporation", exchange="PNK">
89
- #>> #<struct Ticker::Stock symbol="YAHOF", name="YAHOO JAPAN CORP", exchange="PNK">
90
- #>> #<struct Ticker::Stock symbol="YOJ.SG", name="YAHOO JAPAN", exchange="STU">
91
- #>> #<struct Ticker::Stock symbol="YHO.SG", name="YAHOO", exchange="STU">
92
- #>> #<struct Ticker::Stock symbol="YHOO.BA", name="Yahoo! Inc.", exchange="BUE">
93
- #>> #<struct Ticker::Stock symbol="YHO.DU", name="YAHOO", exchange="DUS">
94
- #>> #<struct Ticker::Stock symbol="YHO.HM", name="YAHOO", exchange="HAM">
95
- #>> #<struct Ticker::Stock symbol="YHO.BE", name="YAHOO", exchange="BER">
96
- ```
97
-
98
- ## Copyright
99
-
100
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
101
- It is free software and may be redistributed under the terms specified in the LICENSE file.
102
-
103
- ## License
104
-
105
- Released under the MIT license.
106
-
107
- http://www.opensource.org/licenses/mit-license.php
108
-
109
- > Permission is hereby granted, free of charge, to any person obtaining a copy
110
- > of this software and associated documentation files (the "Software"), to deal
111
- > in the Software without restriction, including without limitation the rights
112
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
113
- > copies of the Software, and to permit persons to whom the Software is
114
- > furnished to do so, subject to the following conditions:
115
- >
116
- > The above copyright notice and this permission notice shall be included in
117
- > all copies or substantial portions of the Software.
118
- >
119
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
120
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
121
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
122
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
123
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
124
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
125
- > THE SOFTWARE.
@@ -1,32 +0,0 @@
1
- # Obligation
2
-
3
- TBD...
4
-
5
- ## Copyright
6
-
7
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
8
- It is free software and may be redistributed under the terms specified in the LICENSE file.
9
-
10
- ## License
11
-
12
- Released under the MIT license.
13
-
14
- http://www.opensource.org/licenses/mit-license.php
15
-
16
- > Permission is hereby granted, free of charge, to any person obtaining a copy
17
- > of this software and associated documentation files (the "Software"), to deal
18
- > in the Software without restriction, including without limitation the rights
19
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
- > copies of the Software, and to permit persons to whom the Software is
21
- > furnished to do so, subject to the following conditions:
22
- >
23
- > The above copyright notice and this permission notice shall be included in
24
- > all copies or substantial portions of the Software.
25
- >
26
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
- > THE SOFTWARE.
@@ -1,217 +0,0 @@
1
- # Promises, Promises...
2
-
3
- A promise is the most powerful and versatile of the concurrency objects in this library.
4
- Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
5
- and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications.
6
-
7
- > A promise represents the eventual value returned from the single completion of an operation.
8
-
9
- Promises are similar to futures and share many of the same behaviours. Promises are far more robust,
10
- however. Promises can be chained in a tree structure where each promise may have zero or more children.
11
- Promises are chained using the `then` method. The result of a call to `then` is always another promise.
12
- Promises are resolved asynchronously in the order they are added to the tree. Parents are guaranteed
13
- to be resolved before their children. The result of each promise is passed to each of its children
14
- upon resolution. When a promise is rejected all its children will be summarily rejected.
15
-
16
- Promises have three possible states: *pending*, *rejected*, and *fulfilled*. When a promise is created it is set
17
- to *pending* and will remain in that state until processing is complete. A completed promise is either *rejected*,
18
- indicating that an exception was thrown during processing, or *fulfilled*, indicating it succeeded. If a promise is
19
- *fulfilled* its `value` will be updated to reflect the result of the operation. If *rejected* the `reason` will
20
- be updated with a reference to the thrown exception. The predicate methods `pending?`, `rejected`, and `fulfilled?`
21
- can be called at any time to obtain the state of the promise, as can the `state` method, which returns a symbol.
22
-
23
- Retrieving the value of a promise is done through the `value` (alias: `deref`) method. Obtaining the value of
24
- a promise is a potentially blocking operation. When a promise is *rejected* a call to `value` will return `nil`
25
- immediately. When a promise is *fulfilled* a call to `value` will immediately return the current value.
26
- When a promise is *pending* a call to `value` will block until the promise is either *rejected* or *fulfilled*.
27
- A *timeout* value can be passed to `value` to limit how long the call will block. If `nil` the call will
28
- block indefinitely. If `0` the call will not block. Any other integer or float value will indicate the
29
- maximum number of seconds to block.
30
-
31
- Promises run on the global thread pool.
32
-
33
- ## Examples
34
-
35
- Start by requiring promises
36
-
37
- ```ruby
38
- require 'concurrent'
39
- ```
40
-
41
- Then create one
42
-
43
- ```ruby
44
- p = Promise.new("Jerry", "D'Antonio") do |first, last|
45
- "#{last}, #{first}"
46
- end
47
- ```
48
-
49
- Promises can be chained using the `then` method. The `then` method
50
- accepts a block but no arguments. The result of the each promise is
51
- passed as the block argument to chained promises
52
-
53
- ```ruby
54
- p = Concurrent::Promise.new(10){|x| x * 2}.then{|result| result - 10 }
55
- ```
56
-
57
- And so on, and so on, and so on...
58
-
59
- ```ruby
60
- p = Concurrent::Promise.new(10){|x| x * 2}.
61
- then{|result| result - 10 }.
62
- then{|result| result * 3 }.
63
- then{|result| result % 5 }
64
- ```
65
-
66
- Promises are executed asynchronously so a newly-created promise *should* always be in the pending state
67
-
68
- ```ruby
69
- p = Concurrent::Promise.new{ "Hello, world!" }
70
- p.state #=> :pending
71
- p.pending? #=> true
72
- ```
73
-
74
- Wait a little bit, and the promise will resolve and provide a value
75
-
76
- ```ruby
77
- p = Concurrent::Promise.new{ "Hello, world!" }
78
- sleep(0.1)
79
-
80
- p.state #=> :fulfilled
81
- p.fulfilled? #=> true
82
- p.value #=> "Hello, world!"
83
- ```
84
-
85
- If an exception occurs, the promise will be rejected and will provide
86
- a reason for the rejection
87
-
88
- ```ruby
89
- p = Concurrent::Promise.new{ raise StandardError.new("Here comes the Boom!") }
90
- sleep(0.1)
91
-
92
- p.state #=> :rejected
93
- p.rejected? #=> true
94
- p.reason #=> "#<StandardError: Here comes the Boom!>"
95
- ```
96
-
97
- ### Rejection
98
-
99
- Much like the economy, rejection exhibits a trickle-down effect. When
100
- a promise is rejected all its children will be rejected
101
-
102
- ```ruby
103
- p = [ Concurrent::Promise.new{ Thread.pass; raise StandardError } ]
104
-
105
- 10.times{|i| p << p.first.then{ i } }
106
- sleep(0.1)
107
-
108
- p.length #=> 11
109
- p.first.state #=> :rejected
110
- p.last.state #=> :rejected
111
- ```
112
-
113
- Once a promise is rejected it will not accept any children. Calls
114
- to `then` will continually return `self`
115
-
116
- ```ruby
117
- p = Concurrent::Promise.new{ raise StandardError }
118
- sleep(0.1)
119
-
120
- p.object_id #=> 32960556
121
- p.then{}.object_id #=> 32960556
122
- p.then{}.object_id #=> 32960556
123
- ```
124
-
125
- ### Error Handling
126
-
127
- Promises support error handling callbacks is a style mimicing Ruby's
128
- own exception handling mechanism, namely `rescue`
129
-
130
- ```ruby
131
- Concurrent::Promise.new{ "dangerous operation..." }.rescue{|ex| puts "Bam!" }
132
-
133
- # -or- (for the Java/C# crowd)
134
- Concurrent::Promise.new{ "dangerous operation..." }.catch{|ex| puts "Boom!" }
135
-
136
- # -or- (for the hipsters)
137
- Concurrent::Promise.new{ "dangerous operation..." }.on_error{|ex| puts "Pow!" }
138
- ```
139
-
140
- As with Ruby's `rescue` mechanism, a promise's `rescue` method can
141
- accept an optional Exception class argument (defaults to `Exception`
142
- when not specified)
143
-
144
- ```ruby
145
- Concurrent::Promise.new{ "dangerous operation..." }.rescue(ArgumentError){|ex| puts "Bam!" }
146
- ```
147
-
148
- Calls to `rescue` can also be chained
149
-
150
- ```ruby
151
- Concurrent::Promise.new{ "dangerous operation..." }.
152
- rescue(ArgumentError){|ex| puts "Bam!" }.
153
- rescue(NoMethodError){|ex| puts "Boom!" }.
154
- rescue(StandardError){|ex| puts "Pow!" }
155
- ```
156
-
157
- When there are multiple `rescue` handlers the first one to match the thrown
158
- exception will be triggered
159
-
160
- ```ruby
161
- Concurrent::Promise.new{ raise NoMethodError }.
162
- rescue(ArgumentError){|ex| puts "Bam!" }.
163
- rescue(NoMethodError){|ex| puts "Boom!" }.
164
- rescue(StandardError){|ex| puts "Pow!" }
165
-
166
- sleep(0.1)
167
-
168
- #=> Boom!
169
- ```
170
-
171
- Trickle-down rejection also applies to rescue handlers. When a promise is rejected,
172
- for any reason, its rescue handlers will be triggered. Rejection of the parent counts.
173
-
174
- ```ruby
175
- Concurrent::Promise.new{ Thread.pass; raise StandardError }.
176
- then{ true }.rescue{ puts 'Boom!' }.
177
- then{ true }.rescue{ puts 'Boom!' }.
178
- then{ true }.rescue{ puts 'Boom!' }.
179
- then{ true }.rescue{ puts 'Boom!' }.
180
- then{ true }.rescue{ puts 'Boom!' }
181
- sleep(0.1)
182
-
183
- #=> Boom!
184
- #=> Boom!
185
- #=> Boom!
186
- #=> Boom!
187
- #=> Boom!
188
- ```
189
-
190
- ## Copyright
191
-
192
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
193
- It is free software and may be redistributed under the terms specified in the LICENSE file.
194
-
195
- ## License
196
-
197
- Released under the MIT license.
198
-
199
- http://www.opensource.org/licenses/mit-license.php
200
-
201
- > Permission is hereby granted, free of charge, to any person obtaining a copy
202
- > of this software and associated documentation files (the "Software"), to deal
203
- > in the Software without restriction, including without limitation the rights
204
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
205
- > copies of the Software, and to permit persons to whom the Software is
206
- > furnished to do so, subject to the following conditions:
207
- >
208
- > The above copyright notice and this permission notice shall be included in
209
- > all copies or substantial portions of the Software.
210
- >
211
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
212
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
213
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
214
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
215
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
216
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
217
- > THE SOFTWARE.