concurrent-ruby 0.4.1 → 0.5.0.pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.