concurrent-ruby 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -21
  3. data/README.md +276 -275
  4. data/lib/concurrent.rb +28 -28
  5. data/lib/concurrent/agent.rb +114 -114
  6. data/lib/concurrent/cached_thread_pool.rb +131 -131
  7. data/lib/concurrent/defer.rb +65 -65
  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 +96 -96
  11. data/lib/concurrent/fixed_thread_pool.rb +99 -99
  12. data/lib/concurrent/functions.rb +120 -120
  13. data/lib/concurrent/future.rb +42 -42
  14. data/lib/concurrent/global_thread_pool.rb +24 -16
  15. data/lib/concurrent/goroutine.rb +29 -29
  16. data/lib/concurrent/null_thread_pool.rb +22 -22
  17. data/lib/concurrent/obligation.rb +67 -67
  18. data/lib/concurrent/promise.rb +174 -174
  19. data/lib/concurrent/reactor.rb +166 -166
  20. data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
  21. data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
  22. data/lib/concurrent/supervisor.rb +105 -105
  23. data/lib/concurrent/thread_pool.rb +76 -76
  24. data/lib/concurrent/utilities.rb +32 -32
  25. data/lib/concurrent/version.rb +3 -3
  26. data/lib/concurrent_ruby.rb +1 -1
  27. data/md/agent.md +123 -123
  28. data/md/defer.md +174 -174
  29. data/md/event.md +32 -32
  30. data/md/executor.md +187 -187
  31. data/md/future.md +83 -83
  32. data/md/goroutine.md +52 -52
  33. data/md/obligation.md +32 -32
  34. data/md/promise.md +227 -227
  35. data/md/thread_pool.md +224 -224
  36. data/spec/concurrent/agent_spec.rb +390 -386
  37. data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
  38. data/spec/concurrent/defer_spec.rb +199 -195
  39. data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
  40. data/spec/concurrent/event_spec.rb +134 -134
  41. data/spec/concurrent/executor_spec.rb +200 -200
  42. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
  43. data/spec/concurrent/functions_spec.rb +217 -217
  44. data/spec/concurrent/future_spec.rb +112 -108
  45. data/spec/concurrent/global_thread_pool_spec.rb +11 -38
  46. data/spec/concurrent/goroutine_spec.rb +67 -67
  47. data/spec/concurrent/null_thread_pool_spec.rb +57 -57
  48. data/spec/concurrent/obligation_shared.rb +132 -132
  49. data/spec/concurrent/promise_spec.rb +316 -312
  50. data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
  51. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
  52. data/spec/concurrent/reactor_spec.rb +364 -364
  53. data/spec/concurrent/supervisor_spec.rb +269 -269
  54. data/spec/concurrent/thread_pool_shared.rb +204 -204
  55. data/spec/concurrent/uses_global_thread_pool_shared.rb +64 -0
  56. data/spec/concurrent/utilities_spec.rb +74 -74
  57. data/spec/spec_helper.rb +32 -32
  58. metadata +17 -19
@@ -1,76 +1,76 @@
1
- require 'functional/behavior'
2
-
3
- require 'concurrent/event'
4
- require 'concurrent/utilities'
5
-
6
- behavior_info(:thread_pool,
7
- running?: 0,
8
- shutdown?: 0,
9
- killed?: 0,
10
- shutdown: 0,
11
- kill: 0,
12
- size: 0,
13
- wait_for_termination: -1,
14
- post: -1,
15
- :<< => 1,
16
- status: 0)
17
-
18
- behavior_info(:global_thread_pool,
19
- post: -1,
20
- :<< => 1)
21
-
22
- module Concurrent
23
-
24
- class ThreadPool
25
-
26
- def initialize
27
- @status = :running
28
- @queue = Queue.new
29
- @termination = Event.new
30
- @pool = []
31
- end
32
-
33
- def running?
34
- return @status == :running
35
- end
36
-
37
- def shutdown?
38
- return @status == :shutdown
39
- end
40
-
41
- def killed?
42
- return @status == :killed
43
- end
44
-
45
- def shutdown
46
- mutex.synchronize do
47
- if @pool.empty?
48
- @status = :shutdown
49
- else
50
- @status = :shuttingdown
51
- @pool.size.times{ @queue << :stop }
52
- end
53
- end
54
- end
55
-
56
- def wait_for_termination(timeout = nil)
57
- if shutdown? || killed?
58
- return true
59
- else
60
- return @termination.wait(timeout)
61
- end
62
- end
63
-
64
- def <<(block)
65
- self.post(&block)
66
- return self
67
- end
68
-
69
- protected
70
-
71
- # @private
72
- def mutex # :nodoc:
73
- @mutex || Mutex.new
74
- end
75
- end
76
- end
1
+ require 'functional/behavior'
2
+
3
+ require 'concurrent/event'
4
+ require 'concurrent/utilities'
5
+
6
+ behavior_info(:thread_pool,
7
+ running?: 0,
8
+ shutdown?: 0,
9
+ killed?: 0,
10
+ shutdown: 0,
11
+ kill: 0,
12
+ size: 0,
13
+ wait_for_termination: -1,
14
+ post: -1,
15
+ :<< => 1,
16
+ status: 0)
17
+
18
+ behavior_info(:global_thread_pool,
19
+ post: -1,
20
+ :<< => 1)
21
+
22
+ module Concurrent
23
+
24
+ class ThreadPool
25
+
26
+ def initialize
27
+ @status = :running
28
+ @queue = Queue.new
29
+ @termination = Event.new
30
+ @pool = []
31
+ end
32
+
33
+ def running?
34
+ return @status == :running
35
+ end
36
+
37
+ def shutdown?
38
+ return @status == :shutdown
39
+ end
40
+
41
+ def killed?
42
+ return @status == :killed
43
+ end
44
+
45
+ def shutdown
46
+ mutex.synchronize do
47
+ if @pool.empty?
48
+ @status = :shutdown
49
+ else
50
+ @status = :shuttingdown
51
+ @pool.size.times{ @queue << :stop }
52
+ end
53
+ end
54
+ end
55
+
56
+ def wait_for_termination(timeout = nil)
57
+ if shutdown? || killed?
58
+ return true
59
+ else
60
+ return @termination.wait(timeout)
61
+ end
62
+ end
63
+
64
+ def <<(block)
65
+ self.post(&block)
66
+ return self
67
+ end
68
+
69
+ protected
70
+
71
+ # @private
72
+ def mutex # :nodoc:
73
+ @mutex || Mutex.new
74
+ end
75
+ end
76
+ end
@@ -1,32 +1,32 @@
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, &block)
28
- Timeout::timeout(timeout) {
29
- synchronize(&block)
30
- }
31
- end
32
- end
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, &block)
28
+ Timeout::timeout(timeout) {
29
+ synchronize(&block)
30
+ }
31
+ end
32
+ end
@@ -1,3 +1,3 @@
1
- module Concurrent
2
- VERSION = '0.2.1'
3
- end
1
+ module Concurrent
2
+ VERSION = '0.2.2'
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.