concurrent-ruby 0.1.0 → 0.1.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/LICENSE +21 -21
  2. data/README.md +279 -224
  3. data/lib/concurrent.rb +27 -20
  4. data/lib/concurrent/agent.rb +106 -130
  5. data/lib/concurrent/cached_thread_pool.rb +130 -122
  6. data/lib/concurrent/defer.rb +67 -69
  7. data/lib/concurrent/drb_async_demux.rb +72 -0
  8. data/lib/concurrent/event.rb +60 -60
  9. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  10. data/lib/concurrent/executor.rb +87 -0
  11. data/lib/concurrent/fixed_thread_pool.rb +89 -89
  12. data/lib/concurrent/functions.rb +120 -0
  13. data/lib/concurrent/future.rb +52 -42
  14. data/lib/concurrent/global_thread_pool.rb +3 -3
  15. data/lib/concurrent/goroutine.rb +29 -25
  16. data/lib/concurrent/obligation.rb +67 -121
  17. data/lib/concurrent/promise.rb +172 -194
  18. data/lib/concurrent/reactor.rb +162 -0
  19. data/lib/concurrent/smart_mutex.rb +66 -0
  20. data/lib/concurrent/tcp_sync_demux.rb +96 -0
  21. data/lib/concurrent/thread_pool.rb +65 -61
  22. data/lib/concurrent/utilities.rb +34 -0
  23. data/lib/concurrent/version.rb +3 -3
  24. data/lib/concurrent_ruby.rb +1 -1
  25. data/md/agent.md +123 -123
  26. data/md/defer.md +174 -174
  27. data/md/event.md +32 -32
  28. data/md/executor.md +176 -0
  29. data/md/future.md +83 -83
  30. data/md/goroutine.md +52 -52
  31. data/md/obligation.md +32 -32
  32. data/md/promise.md +225 -225
  33. data/md/thread_pool.md +197 -197
  34. data/spec/concurrent/agent_spec.rb +376 -405
  35. data/spec/concurrent/cached_thread_pool_spec.rb +112 -112
  36. data/spec/concurrent/defer_spec.rb +209 -199
  37. data/spec/concurrent/event_machine_defer_proxy_spec.rb +250 -246
  38. data/spec/concurrent/event_spec.rb +134 -134
  39. data/spec/concurrent/executor_spec.rb +146 -0
  40. data/spec/concurrent/fixed_thread_pool_spec.rb +84 -84
  41. data/spec/concurrent/functions_spec.rb +57 -0
  42. data/spec/concurrent/future_spec.rb +125 -115
  43. data/spec/concurrent/goroutine_spec.rb +67 -52
  44. data/spec/concurrent/obligation_shared.rb +121 -121
  45. data/spec/concurrent/promise_spec.rb +299 -310
  46. data/spec/concurrent/smart_mutex_spec.rb +234 -0
  47. data/spec/concurrent/thread_pool_shared.rb +209 -209
  48. data/spec/concurrent/utilities_spec.rb +74 -0
  49. data/spec/spec_helper.rb +21 -19
  50. metadata +38 -14
  51. checksums.yaml +0 -7
@@ -0,0 +1,34 @@
1
+ module Kernel
2
+
3
+ # Perform the given block as though it were an atomic operation. This means
4
+ # that the Ruby scheduler cannot premept the block and context switch to
5
+ # another thread. Basically a light wrapper around Ruby's Fiber class.
6
+ #
7
+ # @note Be very careful about what operations you perform within an atomic
8
+ # block. Blocking operations such as I/O should *never* occur within an
9
+ # atomic block. In those cases the entire Ruby VM will lock until the
10
+ # blocking operation is complete. This would be bad.
11
+ #
12
+ # @yield calls the block
13
+ # @yieldparam args an arbitrary set of block arguments
14
+ #
15
+ # @param [Array] zero more more optional arguments to pass to the block
16
+ def atomic(*args)
17
+ raise ArgumentError.new('no block given') unless block_given?
18
+ return Fiber.new {
19
+ yield(*args)
20
+ }.resume
21
+ end
22
+ module_function :atomic
23
+ end
24
+
25
+ class Mutex
26
+
27
+ def sync_with_timeout(timeout)
28
+ Timeout::timeout(timeout) {
29
+ self.synchronize {
30
+ yield
31
+ }
32
+ }
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
- module Concurrent
2
- VERSION = '0.1.0'
3
- end
1
+ module Concurrent
2
+ VERSION = '0.1.1.pre.1'
3
+ end
@@ -1 +1 @@
1
- require 'concurrent'
1
+ require 'concurrent'
@@ -1,123 +1,123 @@
1
- # Secret Agent Man
2
-
3
- Agents are inspired by [Clojure's](http://clojure.org/) [agent](http://clojure.org/agents) keyword.
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. Agents 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
- Agents also implement Ruby's [Observable](http://ruby-doc.org/stdlib-1.9.3/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
- ## Examples
30
-
31
- A simple example:
32
-
33
- ```ruby
34
- require 'concurrent'
35
-
36
- score = Concurrent::Agent.new(10)
37
- score.value #=> 10
38
-
39
- score << proc{|current| current + 100 }
40
- sleep(0.1)
41
- score.value #=> 110
42
-
43
- score << proc{|current| current * 2 }
44
- sleep(0.1)
45
- deref score #=> 220
46
-
47
- score << proc{|current| current - 50 }
48
- sleep(0.1)
49
- score.value #=> 170
50
- ```
51
-
52
- With validation and error handling:
53
-
54
- ```ruby
55
- score = agent(0).validate{|value| value <= 1024 }.
56
- rescue(NoMethodError){|ex| puts "Bam!" }.
57
- rescue(ArgumentError){|ex| puts "Pow!" }.
58
- rescue{|ex| puts "Boom!" }
59
- score.value #=> 0
60
-
61
- score << proc{|current| current + 2048 }
62
- sleep(0.1)
63
- score.value #=> 0
64
-
65
- score << proc{|current| raise ArgumentError }
66
- sleep(0.1)
67
- #=> puts "Pow!"
68
- score.value #=> 0
69
-
70
- score << proc{|current| current + 100 }
71
- sleep(0.1)
72
- score.value #=> 100
73
- ```
74
-
75
- With observation:
76
-
77
- ```ruby
78
- bingo = Class.new{
79
- def update(time, score)
80
- puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
81
- end
82
- }.new
83
-
84
- score = agent(0)
85
- score.add_observer(bingo)
86
-
87
- score << proc{|current| sleep(0.1); current += 30 }
88
- score << proc{|current| sleep(0.1); current += 30 }
89
- score << proc{|current| sleep(0.1); current += 30 }
90
- score << proc{|current| sleep(0.1); current += 30 }
91
-
92
- sleep(1)
93
- #=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
94
- ```
95
-
96
- ## Copyright
97
-
98
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
99
- It is free software and may be redistributed under the terms specified in the LICENSE file.
100
-
101
- ## License
102
-
103
- Released under the MIT license.
104
-
105
- http://www.opensource.org/licenses/mit-license.php
106
-
107
- > Permission is hereby granted, free of charge, to any person obtaining a copy
108
- > of this software and associated documentation files (the "Software"), to deal
109
- > in the Software without restriction, including without limitation the rights
110
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
111
- > copies of the Software, and to permit persons to whom the Software is
112
- > furnished to do so, subject to the following conditions:
113
- >
114
- > The above copyright notice and this permission notice shall be included in
115
- > all copies or substantial portions of the Software.
116
- >
117
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
118
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
119
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
120
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
121
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
122
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
123
- > THE SOFTWARE.
1
+ # Secret Agent Man
2
+
3
+ Agents are inspired by [Clojure's](http://clojure.org/) [agent](http://clojure.org/agents) keyword.
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. Agents 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
+ Agents also implement Ruby's [Observable](http://ruby-doc.org/stdlib-1.9.3/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
+ ## Examples
30
+
31
+ A simple example:
32
+
33
+ ```ruby
34
+ require 'concurrent'
35
+
36
+ score = Concurrent::Agent.new(10)
37
+ score.value #=> 10
38
+
39
+ score << proc{|current| current + 100 }
40
+ sleep(0.1)
41
+ score.value #=> 110
42
+
43
+ score << proc{|current| current * 2 }
44
+ sleep(0.1)
45
+ deref score #=> 220
46
+
47
+ score << proc{|current| current - 50 }
48
+ sleep(0.1)
49
+ score.value #=> 170
50
+ ```
51
+
52
+ With validation and error handling:
53
+
54
+ ```ruby
55
+ score = agent(0).validate{|value| value <= 1024 }.
56
+ rescue(NoMethodError){|ex| puts "Bam!" }.
57
+ rescue(ArgumentError){|ex| puts "Pow!" }.
58
+ rescue{|ex| puts "Boom!" }
59
+ score.value #=> 0
60
+
61
+ score << proc{|current| current + 2048 }
62
+ sleep(0.1)
63
+ score.value #=> 0
64
+
65
+ score << proc{|current| raise ArgumentError }
66
+ sleep(0.1)
67
+ #=> puts "Pow!"
68
+ score.value #=> 0
69
+
70
+ score << proc{|current| current + 100 }
71
+ sleep(0.1)
72
+ score.value #=> 100
73
+ ```
74
+
75
+ With observation:
76
+
77
+ ```ruby
78
+ bingo = Class.new{
79
+ def update(time, score)
80
+ puts "Bingo! [score: #{score}, time: #{time}]" if score >= 100
81
+ end
82
+ }.new
83
+
84
+ score = agent(0)
85
+ score.add_observer(bingo)
86
+
87
+ score << proc{|current| sleep(0.1); current += 30 }
88
+ score << proc{|current| sleep(0.1); current += 30 }
89
+ score << proc{|current| sleep(0.1); current += 30 }
90
+ score << proc{|current| sleep(0.1); current += 30 }
91
+
92
+ sleep(1)
93
+ #=> Bingo! [score: 120, time: 2013-07-22 21:26:08 -0400]
94
+ ```
95
+
96
+ ## Copyright
97
+
98
+ *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
99
+ It is free software and may be redistributed under the terms specified in the LICENSE file.
100
+
101
+ ## License
102
+
103
+ Released under the MIT license.
104
+
105
+ http://www.opensource.org/licenses/mit-license.php
106
+
107
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
108
+ > of this software and associated documentation files (the "Software"), to deal
109
+ > in the Software without restriction, including without limitation the rights
110
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
111
+ > copies of the Software, and to permit persons to whom the Software is
112
+ > furnished to do so, subject to the following conditions:
113
+ >
114
+ > The above copyright notice and this permission notice shall be included in
115
+ > all copies or substantial portions of the Software.
116
+ >
117
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
118
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
119
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
120
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
121
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
122
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
123
+ > THE SOFTWARE.
@@ -1,174 +1,174 @@
1
- # I Can't Think of a Movie or Music Reference for Defer
2
-
3
- In the pantheon of concurrency objects a `Defer` sits somewhere between `Future` and `Promise`.
4
- Inspired by [EventMachine's *defer* method](https://github.com/eventmachine/eventmachine/wiki/EM::Deferrable-and-EM.defer),
5
- a `Defer` can be considered a non-blocking `Future` or a simplified, non-blocking `Promise`.
6
-
7
- Unlike `Future` and `Promise` a defer is non-blocking. The deferred *operation* is performed on another
8
- thread. If the *operation* is successful an optional *callback* is called on the same thread as the *operation*.
9
- The result of the *operation* is passed to the *callbacl*. If the *operation* fails (by raising an exception)
10
- then an optional *errorback* (error callback) is called on
11
- the same thread as the *operation*. The raised exception is passed to the *errorback*. The calling thread is
12
- never aware of the result of the *operation*. This approach fits much more cleanly within an
13
- [event-driven](http://en.wikipedia.org/wiki/Event-driven_programming) application.
14
-
15
- The operation of a `Defer` can easily be simulated using either `Future` or `Promise` and traditional branching
16
- (if/then/else) logic. This approach works but it is more verbose and partitions the work across two threads.
17
- Whenever you find yourself checking the result of a `Future` or a `Promise` then branching based on the result,
18
- consider a `Defer` instead.
19
-
20
- For programmer convenience there are two syntaxes for creating and running a `Defer`. One is idiomatic of Ruby
21
- and uses chained method calls. The other is more isiomatic of [functional programming](http://en.wikipedia.org/wiki/Concurrentprogramming)
22
- and passes one or more `proc` objects as arguments. Do not mix syntaxes on a single `Defer` invocation.
23
-
24
- ## Examples
25
-
26
- A simple `Defer` using idiomatic Ruby syntax:
27
-
28
- ```ruby
29
- require 'concurrent'
30
-
31
- deferred = Concurrent::Defer.new{ puts 'w00t!' }
32
- # when using idiomatic syntax the #go method must be called
33
- deferred.go
34
- sleep(0.1)
35
-
36
- #=> 'w00t!'
37
- ```
38
-
39
- A simple `Defer` using functional programming syntax:
40
-
41
- ```ruby
42
- operation = proc{ puts 'w00t!' }
43
- Concurrent::Defer.new(operation) # NOTE: a call to #go is unnecessary
44
- sleep(0.1)
45
-
46
- #=> 'w00t!'
47
-
48
- defer(operation)
49
- sleep(0.1)
50
-
51
- #=> 'w00t!'
52
- ```
53
-
54
- Adding a *callback*:
55
-
56
- ```ruby
57
- Concurrent::Defer.new{ "Jerry D'Antonio" }.
58
- then{|result| puts "Hello, #{result}!" }.
59
- go
60
-
61
- #=> Hello, Jerry D'Antonio!
62
-
63
- operation = proc{ "Jerry D'Antonio" }
64
- callback = proc{|result| puts "Hello, #{result}!" }
65
- defer(operation, callback, nil)
66
- sleep(0.1)
67
-
68
- #=> Hello, Jerry D'Antonio!
69
- ```
70
-
71
- Adding an *errorback*:
72
-
73
- ```ruby
74
- Concurrent::Defer.new{ raise StandardError.new('Boom!') }.
75
- rescue{|ex| puts ex.message }.
76
- go
77
- sleep(0.1)
78
-
79
- #=> "Boom!"
80
-
81
- operation = proc{ raise StandardError.new('Boom!') }
82
- errorback = proc{|ex| puts ex.message }
83
- defer(operation, nil, errorback)
84
-
85
- #=> "Boom!"
86
- ```
87
-
88
- Putting it all together:
89
-
90
- ```ruby
91
- Concurrent::Defer.new{ "Jerry D'Antonio" }.
92
- then{|result| puts "Hello, #{result}!" }.
93
- rescue{|ex| puts ex.message }.
94
- go
95
-
96
- #=> Hello, Jerry D'Antonio!
97
-
98
- operation = proc{ raise StandardError.new('Boom!') }
99
- callback = proc{|result| puts result }
100
- errorback = proc{|ex| puts ex.message }
101
- defer(operation, callback, errorback)
102
- sleep(0.1)
103
-
104
- #=> "Boom!"
105
- ```
106
-
107
- Crossing the streams:
108
-
109
- ```ruby
110
- operation = proc{ true }
111
- callback = proc{|result| puts result }
112
- errorback = proc{|ex| puts ex.message }
113
-
114
- Concurrent::Defer.new(operation, nil, nil){ false }
115
- #=> ArgumentError: two operations given
116
-
117
- defer(nil, callback, errorback)
118
- # => ArgumentError: no operation given
119
-
120
- Concurrent::Defer.new.go
121
- # => ArgumentError: no operation given
122
-
123
- defer(nil, nil, nil)
124
- # => ArgumentError: no operation given
125
-
126
- Concurrent::Defer.new(operation, nil, nil).
127
- then{|result| puts result }.
128
- go
129
- #=> Concurrent::IllegalMethodCallError: the defer is already running
130
-
131
- defer(callback, nil, nil).then{|result| puts result }
132
- #=> Concurrent::IllegalMethodCallError: the defer is already running
133
-
134
- Concurrent::Defer.new{ true }.
135
- then{|result| puts "Boom!" }.
136
- then{|result| puts "Bam!" }.
137
- go
138
- #=> Concurrent::IllegalMethodCallError: a callback has already been provided
139
-
140
- Concurrent::Defer.new{ raise StandardError }.
141
- rescue{|ex| puts "Boom!" }.
142
- rescue{|ex| puts "Bam!" }.
143
- go
144
- #=> Concurrent::IllegalMethodCallError: a errorback has already been provided
145
- ```
146
-
147
- ## Copyright
148
-
149
- *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
150
- It is free software and may be redistributed under the terms specified in the LICENSE file.
151
-
152
- ## License
153
-
154
- Released under the MIT license.
155
-
156
- http://www.opensource.org/licenses/mit-license.php
157
-
158
- > Permission is hereby granted, free of charge, to any person obtaining a copy
159
- > of this software and associated documentation files (the "Software"), to deal
160
- > in the Software without restriction, including without limitation the rights
161
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
162
- > copies of the Software, and to permit persons to whom the Software is
163
- > furnished to do so, subject to the following conditions:
164
- >
165
- > The above copyright notice and this permission notice shall be included in
166
- > all copies or substantial portions of the Software.
167
- >
168
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
169
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
170
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
171
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
172
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
173
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
174
- > THE SOFTWARE.
1
+ # I Can't Think of a Movie or Music Reference for Defer
2
+
3
+ In the pantheon of concurrency objects a `Defer` sits somewhere between `Future` and `Promise`.
4
+ Inspired by [EventMachine's *defer* method](https://github.com/eventmachine/eventmachine/wiki/EM::Deferrable-and-EM.defer),
5
+ a `Defer` can be considered a non-blocking `Future` or a simplified, non-blocking `Promise`.
6
+
7
+ Unlike `Future` and `Promise` a defer is non-blocking. The deferred *operation* is performed on another
8
+ thread. If the *operation* is successful an optional *callback* is called on the same thread as the *operation*.
9
+ The result of the *operation* is passed to the *callbacl*. If the *operation* fails (by raising an exception)
10
+ then an optional *errorback* (error callback) is called on
11
+ the same thread as the *operation*. The raised exception is passed to the *errorback*. The calling thread is
12
+ never aware of the result of the *operation*. This approach fits much more cleanly within an
13
+ [event-driven](http://en.wikipedia.org/wiki/Event-driven_programming) application.
14
+
15
+ The operation of a `Defer` can easily be simulated using either `Future` or `Promise` and traditional branching
16
+ (if/then/else) logic. This approach works but it is more verbose and partitions the work across two threads.
17
+ Whenever you find yourself checking the result of a `Future` or a `Promise` then branching based on the result,
18
+ consider a `Defer` instead.
19
+
20
+ For programmer convenience there are two syntaxes for creating and running a `Defer`. One is idiomatic of Ruby
21
+ and uses chained method calls. The other is more isiomatic of [functional programming](http://en.wikipedia.org/wiki/Concurrentprogramming)
22
+ and passes one or more `proc` objects as arguments. Do not mix syntaxes on a single `Defer` invocation.
23
+
24
+ ## Examples
25
+
26
+ A simple `Defer` using idiomatic Ruby syntax:
27
+
28
+ ```ruby
29
+ require 'concurrent'
30
+
31
+ deferred = Concurrent::Defer.new{ puts 'w00t!' }
32
+ # when using idiomatic syntax the #go method must be called
33
+ deferred.go
34
+ sleep(0.1)
35
+
36
+ #=> 'w00t!'
37
+ ```
38
+
39
+ A simple `Defer` using functional programming syntax:
40
+
41
+ ```ruby
42
+ operation = proc{ puts 'w00t!' }
43
+ Concurrent::Defer.new(operation) # NOTE: a call to #go is unnecessary
44
+ sleep(0.1)
45
+
46
+ #=> 'w00t!'
47
+
48
+ defer(operation)
49
+ sleep(0.1)
50
+
51
+ #=> 'w00t!'
52
+ ```
53
+
54
+ Adding a *callback*:
55
+
56
+ ```ruby
57
+ Concurrent::Defer.new{ "Jerry D'Antonio" }.
58
+ then{|result| puts "Hello, #{result}!" }.
59
+ go
60
+
61
+ #=> Hello, Jerry D'Antonio!
62
+
63
+ operation = proc{ "Jerry D'Antonio" }
64
+ callback = proc{|result| puts "Hello, #{result}!" }
65
+ defer(operation, callback, nil)
66
+ sleep(0.1)
67
+
68
+ #=> Hello, Jerry D'Antonio!
69
+ ```
70
+
71
+ Adding an *errorback*:
72
+
73
+ ```ruby
74
+ Concurrent::Defer.new{ raise StandardError.new('Boom!') }.
75
+ rescue{|ex| puts ex.message }.
76
+ go
77
+ sleep(0.1)
78
+
79
+ #=> "Boom!"
80
+
81
+ operation = proc{ raise StandardError.new('Boom!') }
82
+ errorback = proc{|ex| puts ex.message }
83
+ defer(operation, nil, errorback)
84
+
85
+ #=> "Boom!"
86
+ ```
87
+
88
+ Putting it all together:
89
+
90
+ ```ruby
91
+ Concurrent::Defer.new{ "Jerry D'Antonio" }.
92
+ then{|result| puts "Hello, #{result}!" }.
93
+ rescue{|ex| puts ex.message }.
94
+ go
95
+
96
+ #=> Hello, Jerry D'Antonio!
97
+
98
+ operation = proc{ raise StandardError.new('Boom!') }
99
+ callback = proc{|result| puts result }
100
+ errorback = proc{|ex| puts ex.message }
101
+ defer(operation, callback, errorback)
102
+ sleep(0.1)
103
+
104
+ #=> "Boom!"
105
+ ```
106
+
107
+ Crossing the streams:
108
+
109
+ ```ruby
110
+ operation = proc{ true }
111
+ callback = proc{|result| puts result }
112
+ errorback = proc{|ex| puts ex.message }
113
+
114
+ Concurrent::Defer.new(operation, nil, nil){ false }
115
+ #=> ArgumentError: two operations given
116
+
117
+ defer(nil, callback, errorback)
118
+ # => ArgumentError: no operation given
119
+
120
+ Concurrent::Defer.new.go
121
+ # => ArgumentError: no operation given
122
+
123
+ defer(nil, nil, nil)
124
+ # => ArgumentError: no operation given
125
+
126
+ Concurrent::Defer.new(operation, nil, nil).
127
+ then{|result| puts result }.
128
+ go
129
+ #=> Concurrent::IllegalMethodCallError: the defer is already running
130
+
131
+ defer(callback, nil, nil).then{|result| puts result }
132
+ #=> Concurrent::IllegalMethodCallError: the defer is already running
133
+
134
+ Concurrent::Defer.new{ true }.
135
+ then{|result| puts "Boom!" }.
136
+ then{|result| puts "Bam!" }.
137
+ go
138
+ #=> Concurrent::IllegalMethodCallError: a callback has already been provided
139
+
140
+ Concurrent::Defer.new{ raise StandardError }.
141
+ rescue{|ex| puts "Boom!" }.
142
+ rescue{|ex| puts "Bam!" }.
143
+ go
144
+ #=> Concurrent::IllegalMethodCallError: a errorback has already been provided
145
+ ```
146
+
147
+ ## Copyright
148
+
149
+ *Concurrent Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
150
+ It is free software and may be redistributed under the terms specified in the LICENSE file.
151
+
152
+ ## License
153
+
154
+ Released under the MIT license.
155
+
156
+ http://www.opensource.org/licenses/mit-license.php
157
+
158
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
159
+ > of this software and associated documentation files (the "Software"), to deal
160
+ > in the Software without restriction, including without limitation the rights
161
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
162
+ > copies of the Software, and to permit persons to whom the Software is
163
+ > furnished to do so, subject to the following conditions:
164
+ >
165
+ > The above copyright notice and this permission notice shall be included in
166
+ > all copies or substantial portions of the Software.
167
+ >
168
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
169
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
170
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
171
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
172
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
173
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
174
+ > THE SOFTWARE.