concurrent-ruby 0.1.1.pre.5 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +1 -48
- data/lib/concurrent.rb +0 -6
- data/lib/concurrent/agent.rb +40 -19
- data/lib/concurrent/cached_thread_pool.rb +11 -10
- data/lib/concurrent/defer.rb +12 -8
- data/lib/concurrent/fixed_thread_pool.rb +6 -12
- data/lib/concurrent/future.rb +20 -8
- data/lib/concurrent/global_thread_pool.rb +0 -13
- data/lib/concurrent/goroutine.rb +1 -5
- data/lib/concurrent/obligation.rb +64 -10
- data/lib/concurrent/promise.rb +60 -38
- data/lib/concurrent/thread_pool.rb +5 -16
- data/lib/concurrent/utilities.rb +0 -8
- data/lib/concurrent/version.rb +1 -1
- data/md/defer.md +4 -4
- data/md/promise.md +0 -2
- data/md/thread_pool.md +0 -27
- data/spec/concurrent/agent_spec.rb +27 -8
- data/spec/concurrent/cached_thread_pool_spec.rb +1 -14
- data/spec/concurrent/defer_spec.rb +21 -17
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +149 -159
- data/spec/concurrent/fixed_thread_pool_spec.rb +3 -2
- data/spec/concurrent/future_spec.rb +10 -3
- data/spec/concurrent/goroutine_spec.rb +0 -15
- data/spec/concurrent/obligation_shared.rb +2 -16
- data/spec/concurrent/promise_spec.rb +13 -15
- data/spec/concurrent/thread_pool_shared.rb +5 -5
- data/spec/concurrent/utilities_spec.rb +1 -30
- data/spec/spec_helper.rb +0 -25
- metadata +7 -28
- data/lib/concurrent/executor.rb +0 -95
- data/lib/concurrent/functions.rb +0 -120
- data/lib/concurrent/null_thread_pool.rb +0 -22
- data/lib/concurrent/reactor.rb +0 -161
- data/lib/concurrent/reactor/drb_async_demux.rb +0 -74
- data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -98
- data/md/executor.md +0 -176
- data/spec/concurrent/executor_spec.rb +0 -200
- data/spec/concurrent/functions_spec.rb +0 -217
- data/spec/concurrent/global_thread_pool_spec.rb +0 -38
- data/spec/concurrent/null_thread_pool_spec.rb +0 -54
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -12
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -12
- data/spec/concurrent/reactor_spec.rb +0 -351
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ace46fc451714785036604e4174b57f81ff2f3c6
|
4
|
+
data.tar.gz: f5278235efdd0cc59ae6645aace41c35ef61c8ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48569321a92ec1304c60d425af98412f56dad24ce4fa6c17b149fa18cf40221df320a95ed806196a017a8f521c3d935433b02b626901ec28de5bac5ddcf92332
|
7
|
+
data.tar.gz: f5731e01e2a559c25d92d11f9c9693fafd8219aa681711962dc8d777a46198fb3e3c0d97e26461d4e4d05e3aad8c8cf8b6aee17fbb31441c83fb36bcc91da920
|
data/README.md
CHANGED
@@ -55,7 +55,6 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
|
|
55
55
|
* Go inspired [Goroutine](https://github.com/jdantonio/concurrent-ruby/blob/master/md/goroutine.md)
|
56
56
|
* JavaScript inspired [Promise](https://github.com/jdantonio/concurrent-ruby/blob/master/md/promise.md)
|
57
57
|
* Java inspired [Thread Pools](https://github.com/jdantonio/concurrent-ruby/blob/master/md/thread_pool.md)
|
58
|
-
* Scheduled task execution with the [Executor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/executor.md) service
|
59
58
|
|
60
59
|
### Is it any good?
|
61
60
|
|
@@ -63,11 +62,7 @@ Several features from Erlang, Go, Clojure, Java, and JavaScript have been implem
|
|
63
62
|
|
64
63
|
### Supported Ruby versions
|
65
64
|
|
66
|
-
MRI
|
67
|
-
fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
|
68
|
-
about JRuby, Rubinius, or the others to fully support them. I can promise good karma and
|
69
|
-
attribution on this page to anyone wishing to take responsibility for verifying compaitibility
|
70
|
-
with any Ruby other than MRI.
|
65
|
+
Optimized for and tested under MRI Ruby 1.9.x and 2.0.
|
71
66
|
|
72
67
|
### Install
|
73
68
|
|
@@ -89,25 +84,6 @@ Once you've installed the gem you must `require` it in your project:
|
|
89
84
|
require 'concurrent'
|
90
85
|
```
|
91
86
|
|
92
|
-
### Kernel Methods
|
93
|
-
|
94
|
-
Many Ruby developers consider it bad form to add function to the global (Kernel) namespace.
|
95
|
-
I don't necessarily agree. If the function acts like a low-level feature of the language
|
96
|
-
I think it is OK to add the method to the `Kernel` module. To support my personal programming
|
97
|
-
style I have chosen to implement `Kernel` methods to instance many of the objects in this
|
98
|
-
library. Out of respect for the larger Ruby community I have made these methods optional.
|
99
|
-
They are not imported with the normal `require 'concurrent'` directive. To import these
|
100
|
-
functions you must import the `concurrent/functions` library.
|
101
|
-
|
102
|
-
```ruby
|
103
|
-
require 'concurrent'
|
104
|
-
score = agent(10) #=> NoMethodError: undefined method `agent' for main:Object
|
105
|
-
|
106
|
-
require 'concurrent/functions'
|
107
|
-
score = agent(10) #=> #<Concurrent::Agent:0x35b2b28 ...
|
108
|
-
score.value #=> 10
|
109
|
-
```
|
110
|
-
|
111
87
|
### Examples
|
112
88
|
|
113
89
|
For complete examples, see the specific documentation linked above. Below are a few examples to whet your appetite.
|
@@ -127,7 +103,6 @@ sleep(0.1)
|
|
127
103
|
|
128
104
|
```ruby
|
129
105
|
require 'concurrent'
|
130
|
-
require 'concurrent/functions'
|
131
106
|
|
132
107
|
score = agent(10)
|
133
108
|
score.value #=> 10
|
@@ -149,7 +124,6 @@ score.value #=> 170
|
|
149
124
|
|
150
125
|
```ruby
|
151
126
|
require 'concurrent'
|
152
|
-
require 'concurrent/functions'
|
153
127
|
|
154
128
|
Concurrent::Defer.new{ "Jerry D'Antonio" }.
|
155
129
|
then{|result| puts "Hello, #{result}!" }.
|
@@ -171,7 +145,6 @@ sleep(0.1)
|
|
171
145
|
|
172
146
|
```ruby
|
173
147
|
require 'concurrent'
|
174
|
-
require 'concurrent/functions'
|
175
148
|
|
176
149
|
count = future{ sleep(1); 10 }
|
177
150
|
count.state #=> :pending
|
@@ -184,7 +157,6 @@ deref count #=> 10
|
|
184
157
|
|
185
158
|
```ruby
|
186
159
|
require 'concurrent'
|
187
|
-
require 'concurrent/functions'
|
188
160
|
|
189
161
|
p = promise("Jerry", "D'Antonio"){|a, b| "#{a} #{b}" }.
|
190
162
|
then{|result| "Hello #{result}." }.
|
@@ -218,25 +190,6 @@ sleep(1)
|
|
218
190
|
@expected #=> 30
|
219
191
|
```
|
220
192
|
|
221
|
-
#### Executor
|
222
|
-
|
223
|
-
```ruby
|
224
|
-
require 'concurrent'
|
225
|
-
|
226
|
-
ec = Concurrent::Executor.run('Foo'){ puts 'Boom!' }
|
227
|
-
|
228
|
-
ec.name #=> "Foo"
|
229
|
-
ec.execution_interval #=> 60 == Concurrent::Executor::EXECUTION_INTERVAL
|
230
|
-
ec.timeout_interval #=> 30 == Concurrent::Executor::TIMEOUT_INTERVAL
|
231
|
-
ec.status #=> "sleep"
|
232
|
-
|
233
|
-
# wait 60 seconds...
|
234
|
-
#=> 'Boom!'
|
235
|
-
#=> ' INFO (2013-08-02 23:20:15) Foo: execution completed successfully'
|
236
|
-
|
237
|
-
ec.kill #=> true
|
238
|
-
```
|
239
|
-
|
240
193
|
## Contributing
|
241
194
|
|
242
195
|
1. Fork it
|
data/lib/concurrent.rb
CHANGED
@@ -6,21 +6,15 @@ require 'concurrent/event'
|
|
6
6
|
|
7
7
|
require 'concurrent/agent'
|
8
8
|
require 'concurrent/defer'
|
9
|
-
require 'concurrent/executor'
|
10
9
|
require 'concurrent/future'
|
11
10
|
require 'concurrent/goroutine'
|
12
11
|
require 'concurrent/promise'
|
13
12
|
require 'concurrent/obligation'
|
14
13
|
require 'concurrent/utilities'
|
15
14
|
|
16
|
-
require 'concurrent/reactor'
|
17
|
-
require 'concurrent/reactor/drb_async_demux'
|
18
|
-
require 'concurrent/reactor/tcp_sync_demux'
|
19
|
-
|
20
15
|
require 'concurrent/thread_pool'
|
21
16
|
require 'concurrent/cached_thread_pool'
|
22
17
|
require 'concurrent/fixed_thread_pool'
|
23
|
-
require 'concurrent/null_thread_pool'
|
24
18
|
|
25
19
|
require 'concurrent/global_thread_pool'
|
26
20
|
|
data/lib/concurrent/agent.rb
CHANGED
@@ -14,7 +14,6 @@ module Concurrent
|
|
14
14
|
# A good example of an agent is a shared incrementing counter, such as the score in a video game.
|
15
15
|
class Agent
|
16
16
|
include Observable
|
17
|
-
include UsesGlobalThreadPool
|
18
17
|
|
19
18
|
TIMEOUT = 5
|
20
19
|
|
@@ -27,20 +26,15 @@ module Concurrent
|
|
27
26
|
@rescuers = []
|
28
27
|
@validator = nil
|
29
28
|
@queue = Queue.new
|
30
|
-
@mutex = Mutex.new
|
31
29
|
|
32
|
-
|
30
|
+
$GLOBAL_THREAD_POOL << proc{ work }
|
33
31
|
end
|
34
32
|
|
35
33
|
def value(timeout = 0) return @value; end
|
36
34
|
alias_method :deref, :value
|
37
35
|
|
38
36
|
def rescue(clazz = Exception, &block)
|
39
|
-
if block_given?
|
40
|
-
@mutex.synchronize do
|
41
|
-
@rescuers << Rescuer.new(clazz, block)
|
42
|
-
end
|
43
|
-
end
|
37
|
+
@rescuers << Rescuer.new(clazz, block) if block_given?
|
44
38
|
return self
|
45
39
|
end
|
46
40
|
alias_method :catch, :rescue
|
@@ -56,10 +50,10 @@ module Concurrent
|
|
56
50
|
|
57
51
|
def post(&block)
|
58
52
|
return @queue.length unless block_given?
|
59
|
-
return
|
53
|
+
return atomic {
|
60
54
|
@queue << block
|
61
55
|
@queue.length
|
62
|
-
|
56
|
+
}
|
63
57
|
end
|
64
58
|
|
65
59
|
def <<(block)
|
@@ -68,7 +62,7 @@ module Concurrent
|
|
68
62
|
end
|
69
63
|
|
70
64
|
def length
|
71
|
-
|
65
|
+
@queue.length
|
72
66
|
end
|
73
67
|
alias_method :size, :length
|
74
68
|
alias_method :count, :length
|
@@ -82,9 +76,7 @@ module Concurrent
|
|
82
76
|
|
83
77
|
# @private
|
84
78
|
def try_rescue(ex) # :nodoc:
|
85
|
-
rescuer = @
|
86
|
-
@rescuers.find{|r| ex.is_a?(r.clazz) }
|
87
|
-
end
|
79
|
+
rescuer = @rescuers.find{|r| ex.is_a?(r.clazz) }
|
88
80
|
rescuer.block.call(ex) if rescuer
|
89
81
|
rescue Exception => e
|
90
82
|
# supress
|
@@ -93,17 +85,18 @@ module Concurrent
|
|
93
85
|
# @private
|
94
86
|
def work # :nodoc:
|
95
87
|
loop do
|
88
|
+
Thread.pass
|
96
89
|
handler = @queue.pop
|
97
90
|
begin
|
98
|
-
result = Timeout.timeout(@timeout)
|
91
|
+
result = Timeout.timeout(@timeout){
|
99
92
|
handler.call(@value)
|
100
|
-
|
93
|
+
}
|
101
94
|
if @validator.nil? || @validator.call(result)
|
102
|
-
|
95
|
+
atomic {
|
103
96
|
@value = result
|
104
97
|
changed
|
105
|
-
|
106
|
-
|
98
|
+
}
|
99
|
+
notify_observers(Time.now, @value)
|
107
100
|
end
|
108
101
|
rescue Exception => ex
|
109
102
|
try_rescue(ex)
|
@@ -112,3 +105,31 @@ module Concurrent
|
|
112
105
|
end
|
113
106
|
end
|
114
107
|
end
|
108
|
+
|
109
|
+
module Kernel
|
110
|
+
|
111
|
+
def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
|
112
|
+
return Concurrent::Agent.new(initial, timeout)
|
113
|
+
end
|
114
|
+
module_function :agent
|
115
|
+
|
116
|
+
def deref(agent, timeout = nil)
|
117
|
+
if agent.respond_to?(:deref)
|
118
|
+
return agent.deref(timeout)
|
119
|
+
elsif agent.respond_to?(:value)
|
120
|
+
return agent.deref(timeout)
|
121
|
+
else
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
module_function :deref
|
126
|
+
|
127
|
+
def post(agent, &block)
|
128
|
+
if agent.respond_to?(:post)
|
129
|
+
return agent.post(&block)
|
130
|
+
else
|
131
|
+
return nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
module_function :deref
|
135
|
+
end
|
@@ -24,11 +24,12 @@ module Concurrent
|
|
24
24
|
@thread_idletime = (opts[:thread_idletime] || DEFAULT_THREAD_IDLETIME).freeze
|
25
25
|
super()
|
26
26
|
@working = 0
|
27
|
+
@mutex = Mutex.new
|
27
28
|
end
|
28
29
|
|
29
30
|
def kill
|
30
31
|
@status = :killed
|
31
|
-
mutex.synchronize do
|
32
|
+
@mutex.synchronize do
|
32
33
|
@pool.each{|t| Thread.kill(t.thread) }
|
33
34
|
end
|
34
35
|
end
|
@@ -41,7 +42,7 @@ module Concurrent
|
|
41
42
|
raise ArgumentError.new('no block given') unless block_given?
|
42
43
|
if running?
|
43
44
|
collect_garbage if @pool.empty?
|
44
|
-
mutex.synchronize do
|
45
|
+
@mutex.synchronize do
|
45
46
|
if @working >= @pool.length
|
46
47
|
create_worker_thread
|
47
48
|
end
|
@@ -55,7 +56,7 @@ module Concurrent
|
|
55
56
|
|
56
57
|
# @private
|
57
58
|
def status # :nodoc:
|
58
|
-
mutex.synchronize do
|
59
|
+
@mutex.synchronize do
|
59
60
|
@pool.collect do |worker|
|
60
61
|
[
|
61
62
|
worker.status,
|
@@ -79,31 +80,31 @@ module Concurrent
|
|
79
80
|
loop do
|
80
81
|
task = @queue.pop
|
81
82
|
|
82
|
-
|
83
|
+
atomic {
|
83
84
|
@working += 1
|
84
85
|
me.status = :working
|
85
|
-
|
86
|
+
}
|
86
87
|
|
87
88
|
if task == :stop
|
88
89
|
me.status = :stopping
|
89
90
|
break
|
90
91
|
else
|
91
92
|
task.last.call(*task.first)
|
92
|
-
|
93
|
+
atomic {
|
93
94
|
@working -= 1
|
94
95
|
me.status = :idle
|
95
96
|
me.idletime = timestamp
|
96
|
-
|
97
|
+
}
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
|
-
|
101
|
+
atomic {
|
101
102
|
@pool.delete(me)
|
102
103
|
if @pool.empty?
|
103
104
|
@termination.set
|
104
105
|
@status = :shutdown unless killed?
|
105
106
|
end
|
106
|
-
|
107
|
+
}
|
107
108
|
end
|
108
109
|
|
109
110
|
@pool << worker
|
@@ -114,7 +115,7 @@ module Concurrent
|
|
114
115
|
@collector = Thread.new do
|
115
116
|
loop do
|
116
117
|
sleep(@gc_interval)
|
117
|
-
mutex.synchronize do
|
118
|
+
@mutex.synchronize do
|
118
119
|
@pool.reject! do |worker|
|
119
120
|
worker.thread.status.nil? ||
|
120
121
|
(worker.status == :idle && @thread_idletime >= delta(worker.idletime, timestamp))
|
data/lib/concurrent/defer.rb
CHANGED
@@ -7,18 +7,14 @@ module Concurrent
|
|
7
7
|
IllegalMethodCallError = Class.new(StandardError)
|
8
8
|
|
9
9
|
class Defer
|
10
|
-
include UsesGlobalThreadPool
|
11
|
-
|
12
|
-
def initialize(opts = {}, &block)
|
13
|
-
operation = opts[:op] || opts[:operation]
|
14
|
-
@callback = opts[:cback] || opts[:callback]
|
15
|
-
@errorback = opts[:eback] || opts[:error] || opts[:errorback]
|
16
|
-
thread_pool = opts[:pool] || opts[:thread_pool]
|
17
10
|
|
11
|
+
def initialize(operation = nil, callback = nil, errorback = nil, &block)
|
18
12
|
raise ArgumentError.new('no operation given') if operation.nil? && ! block_given?
|
19
13
|
raise ArgumentError.new('two operations given') if ! operation.nil? && block_given?
|
20
14
|
|
21
15
|
@operation = operation || block
|
16
|
+
@callback = callback
|
17
|
+
@errorback = errorback
|
22
18
|
|
23
19
|
if operation.nil?
|
24
20
|
@running = false
|
@@ -48,7 +44,7 @@ module Concurrent
|
|
48
44
|
def go
|
49
45
|
return nil if @running
|
50
46
|
@running = true
|
51
|
-
|
47
|
+
$GLOBAL_THREAD_POOL.post { Thread.pass; fulfill }
|
52
48
|
return nil
|
53
49
|
end
|
54
50
|
|
@@ -63,3 +59,11 @@ module Concurrent
|
|
63
59
|
end
|
64
60
|
end
|
65
61
|
end
|
62
|
+
|
63
|
+
module Kernel
|
64
|
+
|
65
|
+
def defer(*args, &block)
|
66
|
+
return Concurrent::Defer.new(*args, &block)
|
67
|
+
end
|
68
|
+
module_function :defer
|
69
|
+
end
|
@@ -26,10 +26,8 @@ module Concurrent
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def kill
|
29
|
-
|
30
|
-
|
31
|
-
@pool.each{|t| Thread.kill(t) }
|
32
|
-
end
|
29
|
+
@status = :killed
|
30
|
+
@pool.each{|t| Thread.kill(t) }
|
33
31
|
end
|
34
32
|
|
35
33
|
def size
|
@@ -52,9 +50,7 @@ module Concurrent
|
|
52
50
|
|
53
51
|
# @private
|
54
52
|
def status # :nodoc:
|
55
|
-
|
56
|
-
@pool.collect{|t| t.status }
|
57
|
-
end
|
53
|
+
@pool.collect{|t| t.status }
|
58
54
|
end
|
59
55
|
|
60
56
|
private
|
@@ -82,11 +78,9 @@ module Concurrent
|
|
82
78
|
def collect_garbage # :nodoc:
|
83
79
|
@collector = Thread.new do
|
84
80
|
sleep(1)
|
85
|
-
|
86
|
-
@pool.
|
87
|
-
|
88
|
-
@pool[i] = create_worker_thread
|
89
|
-
end
|
81
|
+
@pool.size.times do |i|
|
82
|
+
if @pool[i].status.nil?
|
83
|
+
@pool[i] = create_worker_thread
|
90
84
|
end
|
91
85
|
end
|
92
86
|
end
|
data/lib/concurrent/future.rb
CHANGED
@@ -8,17 +8,17 @@ module Concurrent
|
|
8
8
|
|
9
9
|
class Future
|
10
10
|
include Obligation
|
11
|
-
include UsesGlobalThreadPool
|
12
|
-
|
13
11
|
behavior(:future)
|
14
12
|
|
15
13
|
def initialize(*args, &block)
|
14
|
+
|
16
15
|
unless block_given?
|
17
16
|
@state = :fulfilled
|
18
17
|
else
|
19
18
|
@value = nil
|
20
19
|
@state = :pending
|
21
|
-
|
20
|
+
$GLOBAL_THREAD_POOL.post do
|
21
|
+
Thread.pass
|
22
22
|
work(*args, &block)
|
23
23
|
end
|
24
24
|
end
|
@@ -28,15 +28,27 @@ module Concurrent
|
|
28
28
|
|
29
29
|
# @private
|
30
30
|
def work(*args) # :nodoc:
|
31
|
-
|
31
|
+
semaphore.synchronize do
|
32
32
|
begin
|
33
|
-
|
34
|
-
|
33
|
+
atomic {
|
34
|
+
@value = yield(*args)
|
35
|
+
@state = :fulfilled
|
36
|
+
}
|
35
37
|
rescue Exception => ex
|
36
|
-
|
37
|
-
|
38
|
+
atomic {
|
39
|
+
@state = :rejected
|
40
|
+
@reason = ex
|
41
|
+
}
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
47
|
+
|
48
|
+
module Kernel
|
49
|
+
|
50
|
+
def future(*args, &block)
|
51
|
+
return Concurrent::Future.new(*args, &block)
|
52
|
+
end
|
53
|
+
module_function :future
|
54
|
+
end
|