concurrent-ruby 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +45 -42
- data/lib/concurrent.rb +5 -6
- data/lib/concurrent/agent.rb +29 -33
- data/lib/concurrent/cached_thread_pool.rb +26 -105
- data/lib/concurrent/channel.rb +94 -0
- data/lib/concurrent/event.rb +8 -17
- data/lib/concurrent/executor.rb +68 -72
- data/lib/concurrent/fixed_thread_pool.rb +15 -83
- data/lib/concurrent/functions.rb +7 -22
- data/lib/concurrent/future.rb +29 -9
- data/lib/concurrent/null_thread_pool.rb +5 -2
- data/lib/concurrent/obligation.rb +6 -16
- data/lib/concurrent/promise.rb +9 -10
- data/lib/concurrent/runnable.rb +103 -0
- data/lib/concurrent/supervisor.rb +271 -44
- data/lib/concurrent/thread_pool.rb +112 -39
- data/lib/concurrent/version.rb +1 -1
- data/md/executor.md +9 -3
- data/md/goroutine.md +11 -9
- data/md/reactor.md +32 -0
- data/md/supervisor.md +43 -0
- data/spec/concurrent/agent_spec.rb +128 -51
- data/spec/concurrent/cached_thread_pool_spec.rb +33 -47
- data/spec/concurrent/channel_spec.rb +446 -0
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +3 -1
- data/spec/concurrent/event_spec.rb +0 -19
- data/spec/concurrent/executor_spec.rb +167 -119
- data/spec/concurrent/fixed_thread_pool_spec.rb +40 -30
- data/spec/concurrent/functions_spec.rb +0 -20
- data/spec/concurrent/future_spec.rb +88 -0
- data/spec/concurrent/null_thread_pool_spec.rb +23 -2
- data/spec/concurrent/obligation_shared.rb +0 -5
- data/spec/concurrent/promise_spec.rb +9 -10
- data/spec/concurrent/runnable_shared.rb +62 -0
- data/spec/concurrent/runnable_spec.rb +233 -0
- data/spec/concurrent/supervisor_spec.rb +912 -47
- data/spec/concurrent/thread_pool_shared.rb +18 -31
- data/spec/spec_helper.rb +10 -3
- metadata +17 -23
- data/lib/concurrent/defer.rb +0 -65
- data/lib/concurrent/reactor.rb +0 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +0 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -131
- data/lib/concurrent/utilities.rb +0 -32
- data/md/defer.md +0 -174
- data/spec/concurrent/defer_spec.rb +0 -199
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -410
- data/spec/concurrent/reactor_spec.rb +0 -364
- data/spec/concurrent/utilities_spec.rb +0 -74
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
share_examples_for
|
3
|
+
share_examples_for :thread_pool do
|
4
|
+
|
5
|
+
after(:each) do
|
6
|
+
subject.kill
|
7
|
+
sleep(0.1)
|
8
|
+
end
|
4
9
|
|
5
10
|
context '#running?' do
|
6
11
|
|
@@ -25,32 +30,6 @@ share_examples_for 'Thread Pool' do
|
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
28
|
-
context '#shutdown?' do
|
29
|
-
|
30
|
-
it 'returns true if #shutdown is complete' do
|
31
|
-
subject.shutdown
|
32
|
-
sleep(0.1)
|
33
|
-
subject.should be_shutdown
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'returns false when running' do
|
37
|
-
subject.should_not be_shutdown
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context '#killed?' do
|
42
|
-
|
43
|
-
it 'returns true if tasks were killed at shutdown' do
|
44
|
-
subject.post{ sleep(1) }
|
45
|
-
subject.kill
|
46
|
-
subject.should be_killed
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'returns false when running' do
|
50
|
-
subject.should_not be_killed
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
33
|
context '#shutdown' do
|
55
34
|
|
56
35
|
it 'stops accepting new tasks' do
|
@@ -113,6 +92,14 @@ share_examples_for 'Thread Pool' do
|
|
113
92
|
sleep(1)
|
114
93
|
@expected.should be_false
|
115
94
|
end
|
95
|
+
|
96
|
+
it 'kills all threads' do
|
97
|
+
100.times { subject << proc{ sleep(1) } }
|
98
|
+
sleep(0.1)
|
99
|
+
Thread.should_receive(:kill).at_least(subject.size).times
|
100
|
+
subject.kill
|
101
|
+
sleep(0.1)
|
102
|
+
end
|
116
103
|
end
|
117
104
|
|
118
105
|
context '#wait_for_termination' do
|
@@ -150,12 +137,12 @@ share_examples_for 'Thread Pool' do
|
|
150
137
|
end
|
151
138
|
|
152
139
|
it 'returns true when the block is added to the queue' do
|
153
|
-
subject.post{
|
140
|
+
subject.post{ sleep }.should be_true
|
154
141
|
end
|
155
142
|
|
156
143
|
it 'calls the block with the given arguments' do
|
157
144
|
@expected = nil
|
158
|
-
subject.post(1, 2, 3)do |a, b, c|
|
145
|
+
subject.post(1, 2, 3) do |a, b, c|
|
159
146
|
@expected = a + b + c
|
160
147
|
end
|
161
148
|
sleep(0.1)
|
@@ -166,7 +153,7 @@ share_examples_for 'Thread Pool' do
|
|
166
153
|
subject.post{ sleep(1) }
|
167
154
|
subject.shutdown
|
168
155
|
@expected = nil
|
169
|
-
subject.post(1, 2, 3)do |a, b, c|
|
156
|
+
subject.post(1, 2, 3) do |a, b, c|
|
170
157
|
@expected = a + b + c
|
171
158
|
end
|
172
159
|
@expected.should be_nil
|
@@ -181,7 +168,7 @@ share_examples_for 'Thread Pool' do
|
|
181
168
|
it 'rejects the block once shutdown' do
|
182
169
|
subject.shutdown
|
183
170
|
@expected = nil
|
184
|
-
subject.post(1, 2, 3)do |a, b, c|
|
171
|
+
subject.post(1, 2, 3) do |a, b, c|
|
185
172
|
@expected = a + b + c
|
186
173
|
end
|
187
174
|
@expected.should be_nil
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
|
2
9
|
SimpleCov.start do
|
3
10
|
project_name 'concurrent-ruby'
|
4
11
|
add_filter '/md/'
|
@@ -12,8 +19,6 @@ require 'eventmachine'
|
|
12
19
|
require 'concurrent'
|
13
20
|
require 'concurrent/functions'
|
14
21
|
|
15
|
-
require 'functional'
|
16
|
-
|
17
22
|
# import all the support files
|
18
23
|
Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require File.expand_path(f) }
|
19
24
|
|
@@ -27,6 +32,8 @@ RSpec.configure do |config|
|
|
27
32
|
end
|
28
33
|
|
29
34
|
config.after(:each) do
|
35
|
+
Thread.list.each do |thread|
|
36
|
+
thread.kill unless thread == Thread.current
|
37
|
+
end
|
30
38
|
end
|
31
|
-
|
32
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: functional-ruby
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description: |2
|
42
|
-
Modern concurrency tools including agents, futures, promises, thread pools,
|
42
|
+
Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
|
43
43
|
Inspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency patterns.
|
44
44
|
email: jerry.dantonio@gmail.com
|
45
45
|
executables: []
|
@@ -52,7 +52,7 @@ files:
|
|
52
52
|
- LICENSE
|
53
53
|
- lib/concurrent/agent.rb
|
54
54
|
- lib/concurrent/cached_thread_pool.rb
|
55
|
-
- lib/concurrent/
|
55
|
+
- lib/concurrent/channel.rb
|
56
56
|
- lib/concurrent/event.rb
|
57
57
|
- lib/concurrent/event_machine_defer_proxy.rb
|
58
58
|
- lib/concurrent/executor.rb
|
@@ -64,27 +64,25 @@ files:
|
|
64
64
|
- lib/concurrent/null_thread_pool.rb
|
65
65
|
- lib/concurrent/obligation.rb
|
66
66
|
- lib/concurrent/promise.rb
|
67
|
-
- lib/concurrent/
|
68
|
-
- lib/concurrent/reactor/tcp_sync_demux.rb
|
69
|
-
- lib/concurrent/reactor.rb
|
67
|
+
- lib/concurrent/runnable.rb
|
70
68
|
- lib/concurrent/supervisor.rb
|
71
69
|
- lib/concurrent/thread_pool.rb
|
72
|
-
- lib/concurrent/utilities.rb
|
73
70
|
- lib/concurrent/version.rb
|
74
71
|
- lib/concurrent.rb
|
75
72
|
- lib/concurrent_ruby.rb
|
76
73
|
- md/agent.md
|
77
|
-
- md/defer.md
|
78
74
|
- md/event.md
|
79
75
|
- md/executor.md
|
80
76
|
- md/future.md
|
81
77
|
- md/goroutine.md
|
82
78
|
- md/obligation.md
|
83
79
|
- md/promise.md
|
80
|
+
- md/reactor.md
|
81
|
+
- md/supervisor.md
|
84
82
|
- md/thread_pool.md
|
85
83
|
- spec/concurrent/agent_spec.rb
|
86
84
|
- spec/concurrent/cached_thread_pool_spec.rb
|
87
|
-
- spec/concurrent/
|
85
|
+
- spec/concurrent/channel_spec.rb
|
88
86
|
- spec/concurrent/event_machine_defer_proxy_spec.rb
|
89
87
|
- spec/concurrent/event_spec.rb
|
90
88
|
- spec/concurrent/executor_spec.rb
|
@@ -96,13 +94,11 @@ files:
|
|
96
94
|
- spec/concurrent/null_thread_pool_spec.rb
|
97
95
|
- spec/concurrent/obligation_shared.rb
|
98
96
|
- spec/concurrent/promise_spec.rb
|
99
|
-
- spec/concurrent/
|
100
|
-
- spec/concurrent/
|
101
|
-
- spec/concurrent/reactor_spec.rb
|
97
|
+
- spec/concurrent/runnable_shared.rb
|
98
|
+
- spec/concurrent/runnable_spec.rb
|
102
99
|
- spec/concurrent/supervisor_spec.rb
|
103
100
|
- spec/concurrent/thread_pool_shared.rb
|
104
101
|
- spec/concurrent/uses_global_thread_pool_shared.rb
|
105
|
-
- spec/concurrent/utilities_spec.rb
|
106
102
|
- spec/spec_helper.rb
|
107
103
|
homepage: http://www.concurrent-ruby.com
|
108
104
|
licenses:
|
@@ -122,20 +118,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
118
|
version: 1.9.2
|
123
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
120
|
requirements:
|
125
|
-
- - '
|
121
|
+
- - '>'
|
126
122
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
123
|
+
version: 1.3.1
|
128
124
|
requirements: []
|
129
125
|
rubyforge_project:
|
130
|
-
rubygems_version: 2.
|
126
|
+
rubygems_version: 2.1.5
|
131
127
|
signing_key:
|
132
128
|
specification_version: 4
|
133
129
|
summary: Modern concurrency tools including agents, futures, promises, thread pools,
|
134
|
-
|
130
|
+
actors, and more.
|
135
131
|
test_files:
|
136
132
|
- spec/concurrent/agent_spec.rb
|
137
133
|
- spec/concurrent/cached_thread_pool_spec.rb
|
138
|
-
- spec/concurrent/
|
134
|
+
- spec/concurrent/channel_spec.rb
|
139
135
|
- spec/concurrent/event_machine_defer_proxy_spec.rb
|
140
136
|
- spec/concurrent/event_spec.rb
|
141
137
|
- spec/concurrent/executor_spec.rb
|
@@ -147,11 +143,9 @@ test_files:
|
|
147
143
|
- spec/concurrent/null_thread_pool_spec.rb
|
148
144
|
- spec/concurrent/obligation_shared.rb
|
149
145
|
- spec/concurrent/promise_spec.rb
|
150
|
-
- spec/concurrent/
|
151
|
-
- spec/concurrent/
|
152
|
-
- spec/concurrent/reactor_spec.rb
|
146
|
+
- spec/concurrent/runnable_shared.rb
|
147
|
+
- spec/concurrent/runnable_spec.rb
|
153
148
|
- spec/concurrent/supervisor_spec.rb
|
154
149
|
- spec/concurrent/thread_pool_shared.rb
|
155
150
|
- spec/concurrent/uses_global_thread_pool_shared.rb
|
156
|
-
- spec/concurrent/utilities_spec.rb
|
157
151
|
- spec/spec_helper.rb
|
data/lib/concurrent/defer.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/global_thread_pool'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
IllegalMethodCallError = Class.new(StandardError)
|
8
|
-
|
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
|
-
|
18
|
-
raise ArgumentError.new('no operation given') if operation.nil? && ! block_given?
|
19
|
-
raise ArgumentError.new('two operations given') if ! operation.nil? && block_given?
|
20
|
-
|
21
|
-
@operation = operation || block
|
22
|
-
|
23
|
-
if operation.nil?
|
24
|
-
@running = false
|
25
|
-
else
|
26
|
-
self.go
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def then(&block)
|
31
|
-
raise IllegalMethodCallError.new('a callback has already been provided') unless @callback.nil?
|
32
|
-
raise IllegalMethodCallError.new('the defer is already running') if @running
|
33
|
-
raise ArgumentError.new('no block given') unless block_given?
|
34
|
-
@callback = block
|
35
|
-
return self
|
36
|
-
end
|
37
|
-
|
38
|
-
def rescue(&block)
|
39
|
-
raise IllegalMethodCallError.new('a errorback has already been provided') unless @errorback.nil?
|
40
|
-
raise IllegalMethodCallError.new('the defer is already running') if @running
|
41
|
-
raise ArgumentError.new('no block given') unless block_given?
|
42
|
-
@errorback = block
|
43
|
-
return self
|
44
|
-
end
|
45
|
-
alias_method :catch, :rescue
|
46
|
-
alias_method :on_error, :rescue
|
47
|
-
|
48
|
-
def go
|
49
|
-
return nil if @running
|
50
|
-
@running = true
|
51
|
-
Defer.thread_pool.post { fulfill }
|
52
|
-
return nil
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
# @private
|
58
|
-
def fulfill # :nodoc:
|
59
|
-
result = @operation.call
|
60
|
-
@callback.call(result) unless @callback.nil?
|
61
|
-
rescue Exception => ex
|
62
|
-
@errorback.call(ex) unless @errorback.nil?
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
data/lib/concurrent/reactor.rb
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'functional'
|
3
|
-
require 'concurrent/supervisor'
|
4
|
-
|
5
|
-
behavior_info(:sync_event_demux,
|
6
|
-
run: 0,
|
7
|
-
stop: 0,
|
8
|
-
running?: 0,
|
9
|
-
accept: 0,
|
10
|
-
respond: 2)
|
11
|
-
|
12
|
-
behavior_info(:async_event_demux,
|
13
|
-
run: 0,
|
14
|
-
stop: 0,
|
15
|
-
running?: 0,
|
16
|
-
set_reactor: 1)
|
17
|
-
|
18
|
-
behavior_info(:demux_reactor,
|
19
|
-
handle: -2)
|
20
|
-
|
21
|
-
module Concurrent
|
22
|
-
|
23
|
-
class Reactor
|
24
|
-
|
25
|
-
behavior(:demux_reactor)
|
26
|
-
behavior(:runnable)
|
27
|
-
|
28
|
-
RESERVED_EVENTS = [ :stop ]
|
29
|
-
|
30
|
-
EventContext = Struct.new(:event, :args, :callback)
|
31
|
-
|
32
|
-
def initialize(demux = nil)
|
33
|
-
@demux = demux
|
34
|
-
if @demux.nil? || @demux.behaves_as?(:async_event_demux)
|
35
|
-
@sync = false
|
36
|
-
@queue = Queue.new
|
37
|
-
@demux.set_reactor(self) unless @demux.nil?
|
38
|
-
elsif @demux.behaves_as?(:sync_event_demux)
|
39
|
-
@sync = true
|
40
|
-
else
|
41
|
-
raise ArgumentError.new("invalid event demultiplexer '#{@demux}'")
|
42
|
-
end
|
43
|
-
|
44
|
-
@running = false
|
45
|
-
@handlers = Hash.new
|
46
|
-
@mutex = Mutex.new
|
47
|
-
end
|
48
|
-
|
49
|
-
def running?
|
50
|
-
return @running
|
51
|
-
end
|
52
|
-
|
53
|
-
def add_handler(event, &block)
|
54
|
-
raise ArgumentError.new('no block given') unless block_given?
|
55
|
-
event = event.to_sym
|
56
|
-
raise ArgumentError.new("'#{event}' is a reserved event") if RESERVED_EVENTS.include?(event)
|
57
|
-
@mutex.synchronize {
|
58
|
-
@handlers[event] = block
|
59
|
-
}
|
60
|
-
return true
|
61
|
-
end
|
62
|
-
|
63
|
-
def remove_handler(event)
|
64
|
-
handler = @mutex.synchronize {
|
65
|
-
@handlers.delete(event.to_sym)
|
66
|
-
}
|
67
|
-
return ! handler.nil?
|
68
|
-
end
|
69
|
-
|
70
|
-
def stop_on_signal(*signals)
|
71
|
-
signals.each{|signal| Signal.trap(signal){ Thread.new{ self.stop }.abort_on_exception = false}}
|
72
|
-
end
|
73
|
-
|
74
|
-
def handle(event, *args)
|
75
|
-
raise NotImplementedError.new("demultiplexer '#{@demux.class}' is synchronous") if @sync
|
76
|
-
return [:stopped, 'reactor not running'] unless running?
|
77
|
-
context = EventContext.new(event.to_sym, args.dup, Queue.new)
|
78
|
-
@queue.push(context)
|
79
|
-
return context.callback.pop
|
80
|
-
end
|
81
|
-
|
82
|
-
def run
|
83
|
-
raise StandardError.new('already running') if self.running?
|
84
|
-
@sync ? (@running = true; run_sync) : (@running = true; run_async)
|
85
|
-
end
|
86
|
-
alias_method :run, :run
|
87
|
-
|
88
|
-
def stop
|
89
|
-
return true unless self.running?
|
90
|
-
if @sync
|
91
|
-
@demux.stop
|
92
|
-
else
|
93
|
-
@queue.push(:stop)
|
94
|
-
end
|
95
|
-
return true
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def handle_event(context)
|
101
|
-
raise ArgumentError.new('no block given') unless block_given?
|
102
|
-
|
103
|
-
handler = @mutex.synchronize {
|
104
|
-
@handlers[context.event]
|
105
|
-
}
|
106
|
-
|
107
|
-
if handler.nil?
|
108
|
-
response = yield(:noop, "'#{context.event}' handler not found")
|
109
|
-
else
|
110
|
-
begin
|
111
|
-
result = handler.call(*context.args)
|
112
|
-
response = yield(:ok, result)
|
113
|
-
rescue Exception => ex
|
114
|
-
response = yield(:ex, ex)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
return response
|
119
|
-
end
|
120
|
-
|
121
|
-
def finalize_stop
|
122
|
-
@mutex.synchronize do
|
123
|
-
@running = false
|
124
|
-
@demux.stop unless @demux.nil?
|
125
|
-
@demux = nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def run_sync
|
130
|
-
@demux.run
|
131
|
-
|
132
|
-
loop do
|
133
|
-
break unless @demux.running?
|
134
|
-
context = @demux.accept
|
135
|
-
begin
|
136
|
-
if context.nil?
|
137
|
-
@demux.stop
|
138
|
-
else
|
139
|
-
response = handle_event(context) do |result, message|
|
140
|
-
[result, message]
|
141
|
-
end
|
142
|
-
@demux.respond(*response)
|
143
|
-
end
|
144
|
-
rescue Exception => ex
|
145
|
-
@demux.respond(:abend, ex)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
finalize_stop
|
150
|
-
end
|
151
|
-
|
152
|
-
def run_async
|
153
|
-
@demux.run unless @demux.nil?
|
154
|
-
|
155
|
-
loop do
|
156
|
-
context = @queue.pop
|
157
|
-
break if context == :stop
|
158
|
-
handle_event(context) do |result, message|
|
159
|
-
context.callback.push([result, message])
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
finalize_stop
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|