concurrent-ruby 0.2.0 → 0.2.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.
- data/LICENSE +21 -21
- data/README.md +275 -275
- data/lib/concurrent.rb +28 -28
- data/lib/concurrent/agent.rb +114 -114
- data/lib/concurrent/cached_thread_pool.rb +131 -129
- data/lib/concurrent/defer.rb +65 -65
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +96 -95
- data/lib/concurrent/fixed_thread_pool.rb +99 -95
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -42
- data/lib/concurrent/global_thread_pool.rb +16 -16
- data/lib/concurrent/goroutine.rb +29 -29
- data/lib/concurrent/null_thread_pool.rb +22 -22
- data/lib/concurrent/obligation.rb +67 -67
- data/lib/concurrent/promise.rb +174 -174
- data/lib/concurrent/reactor.rb +166 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
- data/lib/concurrent/supervisor.rb +105 -100
- data/lib/concurrent/thread_pool.rb +76 -76
- data/lib/concurrent/utilities.rb +32 -32
- data/lib/concurrent/version.rb +3 -3
- data/lib/concurrent_ruby.rb +1 -1
- data/md/agent.md +123 -123
- data/md/defer.md +174 -174
- data/md/event.md +32 -32
- data/md/executor.md +187 -187
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +227 -227
- data/md/thread_pool.md +224 -224
- data/spec/concurrent/agent_spec.rb +386 -386
- data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
- data/spec/concurrent/defer_spec.rb +195 -195
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +200 -200
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
- data/spec/concurrent/functions_spec.rb +217 -217
- data/spec/concurrent/future_spec.rb +108 -108
- data/spec/concurrent/global_thread_pool_spec.rb +38 -38
- data/spec/concurrent/goroutine_spec.rb +67 -67
- data/spec/concurrent/null_thread_pool_spec.rb +57 -54
- data/spec/concurrent/obligation_shared.rb +132 -132
- data/spec/concurrent/promise_spec.rb +312 -312
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
- data/spec/concurrent/reactor_spec.rb +364 -364
- data/spec/concurrent/supervisor_spec.rb +269 -258
- data/spec/concurrent/thread_pool_shared.rb +204 -204
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +32 -32
- metadata +20 -16
- checksums.yaml +0 -7
data/lib/concurrent/defer.rb
CHANGED
@@ -1,65 +1,65 @@
|
|
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
|
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/event.rb
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'timeout'
|
3
|
-
|
4
|
-
module Concurrent
|
5
|
-
|
6
|
-
class Event
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@set = false
|
10
|
-
@notifier = Queue.new
|
11
|
-
@mutex = Mutex.new
|
12
|
-
@waiting = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def set?
|
16
|
-
return @set == true
|
17
|
-
end
|
18
|
-
|
19
|
-
def set(pulse = false)
|
20
|
-
return true if set?
|
21
|
-
@mutex.synchronize {
|
22
|
-
@set = true
|
23
|
-
while @waiting > 0
|
24
|
-
@notifier << :set
|
25
|
-
@waiting -= 1
|
26
|
-
end
|
27
|
-
@set = ! pulse
|
28
|
-
}
|
29
|
-
return true
|
30
|
-
end
|
31
|
-
|
32
|
-
def reset
|
33
|
-
@mutex.synchronize {
|
34
|
-
@set = false
|
35
|
-
}
|
36
|
-
return true
|
37
|
-
end
|
38
|
-
|
39
|
-
def pulse
|
40
|
-
return set(true)
|
41
|
-
end
|
42
|
-
|
43
|
-
def wait(timeout = nil)
|
44
|
-
return true if set?
|
45
|
-
|
46
|
-
if timeout.nil?
|
47
|
-
@waiting += 1
|
48
|
-
@notifier.pop
|
49
|
-
else
|
50
|
-
Timeout::timeout(timeout) do
|
51
|
-
@waiting += 1
|
52
|
-
@notifier.pop
|
53
|
-
end
|
54
|
-
end
|
55
|
-
return true
|
56
|
-
rescue Timeout::Error
|
57
|
-
return false
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
1
|
+
require 'thread'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
class Event
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@set = false
|
10
|
+
@notifier = Queue.new
|
11
|
+
@mutex = Mutex.new
|
12
|
+
@waiting = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def set?
|
16
|
+
return @set == true
|
17
|
+
end
|
18
|
+
|
19
|
+
def set(pulse = false)
|
20
|
+
return true if set?
|
21
|
+
@mutex.synchronize {
|
22
|
+
@set = true
|
23
|
+
while @waiting > 0
|
24
|
+
@notifier << :set
|
25
|
+
@waiting -= 1
|
26
|
+
end
|
27
|
+
@set = ! pulse
|
28
|
+
}
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset
|
33
|
+
@mutex.synchronize {
|
34
|
+
@set = false
|
35
|
+
}
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
def pulse
|
40
|
+
return set(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
def wait(timeout = nil)
|
44
|
+
return true if set?
|
45
|
+
|
46
|
+
if timeout.nil?
|
47
|
+
@waiting += 1
|
48
|
+
@notifier.pop
|
49
|
+
else
|
50
|
+
Timeout::timeout(timeout) do
|
51
|
+
@waiting += 1
|
52
|
+
@notifier.pop
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return true
|
56
|
+
rescue Timeout::Error
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -1,23 +1,23 @@
|
|
1
|
-
require 'concurrent/global_thread_pool'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
class EventMachineDeferProxy
|
6
|
-
behavior(:global_thread_pool)
|
7
|
-
|
8
|
-
def post(*args, &block)
|
9
|
-
if args.empty?
|
10
|
-
EventMachine.defer(block)
|
11
|
-
else
|
12
|
-
new_block = proc{ block.call(*args) }
|
13
|
-
EventMachine.defer(new_block)
|
14
|
-
end
|
15
|
-
return true
|
16
|
-
end
|
17
|
-
|
18
|
-
def <<(block)
|
19
|
-
EventMachine.defer(block)
|
20
|
-
return self
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
require 'concurrent/global_thread_pool'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
class EventMachineDeferProxy
|
6
|
+
behavior(:global_thread_pool)
|
7
|
+
|
8
|
+
def post(*args, &block)
|
9
|
+
if args.empty?
|
10
|
+
EventMachine.defer(block)
|
11
|
+
else
|
12
|
+
new_block = proc{ block.call(*args) }
|
13
|
+
EventMachine.defer(new_block)
|
14
|
+
end
|
15
|
+
return true
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(block)
|
19
|
+
EventMachine.defer(block)
|
20
|
+
return self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/concurrent/executor.rb
CHANGED
@@ -1,95 +1,96 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
module Executor
|
6
|
-
extend self
|
7
|
-
|
8
|
-
class ExecutionContext
|
9
|
-
attr_reader :name
|
10
|
-
attr_reader :execution_interval
|
11
|
-
attr_reader :timeout_interval
|
12
|
-
|
13
|
-
protected
|
14
|
-
|
15
|
-
def initialize(name, execution_interval, timeout_interval, thread)
|
16
|
-
@name = name
|
17
|
-
@execution_interval = execution_interval
|
18
|
-
@timeout_interval = timeout_interval
|
19
|
-
@thread = thread
|
20
|
-
@thread[:stop] = false
|
21
|
-
end
|
22
|
-
|
23
|
-
public
|
24
|
-
|
25
|
-
def status
|
26
|
-
return @thread.status unless @thread.nil?
|
27
|
-
end
|
28
|
-
|
29
|
-
def join(limit = nil)
|
30
|
-
if @thread.nil?
|
31
|
-
return nil
|
32
|
-
elsif limit.nil?
|
33
|
-
return @thread.join
|
34
|
-
else
|
35
|
-
return @thread.join(limit)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def stop
|
40
|
-
@thread[:stop] = true
|
41
|
-
end
|
42
|
-
|
43
|
-
def kill
|
44
|
-
unless @thread.nil?
|
45
|
-
stop
|
46
|
-
Thread.kill(@thread)
|
47
|
-
@thread = nil
|
48
|
-
end
|
49
|
-
end
|
50
|
-
alias_method :terminate, :kill
|
51
|
-
end
|
52
|
-
|
53
|
-
EXECUTION_INTERVAL = 60
|
54
|
-
TIMEOUT_INTERVAL = 30
|
55
|
-
|
56
|
-
STDOUT_LOGGER = proc do |name, level, msg|
|
57
|
-
print "%5s (%s) %s: %s\n" % [level.upcase, Time.now.strftime("%F %T"), name, msg]
|
58
|
-
end
|
59
|
-
|
60
|
-
def run(name, opts = {})
|
61
|
-
raise ArgumentError.new('no block given') unless block_given?
|
62
|
-
|
63
|
-
execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
64
|
-
timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
|
65
|
-
run_now = opts[:now] || opts[:run_now] || false
|
66
|
-
logger = opts[:logger] || STDOUT_LOGGER
|
67
|
-
block_args = opts[:args] || opts [:arguments] || []
|
68
|
-
|
69
|
-
executor = Thread.new(*block_args) do |*args|
|
70
|
-
sleep(execution_interval) unless run_now == true
|
71
|
-
loop do
|
72
|
-
break if Thread.current[:stop]
|
73
|
-
begin
|
74
|
-
worker = Thread.new{ yield(*args) }
|
75
|
-
worker.abort_on_exception = false
|
76
|
-
if worker.join(timeout_interval).nil?
|
77
|
-
logger.call(name, :warn, "execution timed out after #{timeout_interval} seconds")
|
78
|
-
else
|
79
|
-
logger.call(name, :info, 'execution completed successfully')
|
80
|
-
end
|
81
|
-
rescue Exception => ex
|
82
|
-
logger.call(name, :error, "execution failed with error '#{ex}'")
|
83
|
-
ensure
|
84
|
-
Thread.kill(worker)
|
85
|
-
worker = nil
|
86
|
-
end
|
87
|
-
break if Thread.current[:stop]
|
88
|
-
sleep(execution_interval)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
module Executor
|
6
|
+
extend self
|
7
|
+
|
8
|
+
class ExecutionContext
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :execution_interval
|
11
|
+
attr_reader :timeout_interval
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def initialize(name, execution_interval, timeout_interval, thread)
|
16
|
+
@name = name
|
17
|
+
@execution_interval = execution_interval
|
18
|
+
@timeout_interval = timeout_interval
|
19
|
+
@thread = thread
|
20
|
+
@thread[:stop] = false
|
21
|
+
end
|
22
|
+
|
23
|
+
public
|
24
|
+
|
25
|
+
def status
|
26
|
+
return @thread.status unless @thread.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def join(limit = nil)
|
30
|
+
if @thread.nil?
|
31
|
+
return nil
|
32
|
+
elsif limit.nil?
|
33
|
+
return @thread.join
|
34
|
+
else
|
35
|
+
return @thread.join(limit)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def stop
|
40
|
+
@thread[:stop] = true
|
41
|
+
end
|
42
|
+
|
43
|
+
def kill
|
44
|
+
unless @thread.nil?
|
45
|
+
stop
|
46
|
+
Thread.kill(@thread)
|
47
|
+
@thread = nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias_method :terminate, :kill
|
51
|
+
end
|
52
|
+
|
53
|
+
EXECUTION_INTERVAL = 60
|
54
|
+
TIMEOUT_INTERVAL = 30
|
55
|
+
|
56
|
+
STDOUT_LOGGER = proc do |name, level, msg|
|
57
|
+
print "%5s (%s) %s: %s\n" % [level.upcase, Time.now.strftime("%F %T"), name, msg]
|
58
|
+
end
|
59
|
+
|
60
|
+
def run(name, opts = {})
|
61
|
+
raise ArgumentError.new('no block given') unless block_given?
|
62
|
+
|
63
|
+
execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
64
|
+
timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL
|
65
|
+
run_now = opts[:now] || opts[:run_now] || false
|
66
|
+
logger = opts[:logger] || STDOUT_LOGGER
|
67
|
+
block_args = opts[:args] || opts [:arguments] || []
|
68
|
+
|
69
|
+
executor = Thread.new(*block_args) do |*args|
|
70
|
+
sleep(execution_interval) unless run_now == true
|
71
|
+
loop do
|
72
|
+
break if Thread.current[:stop]
|
73
|
+
begin
|
74
|
+
worker = Thread.new{ yield(*args) }
|
75
|
+
worker.abort_on_exception = false
|
76
|
+
if worker.join(timeout_interval).nil?
|
77
|
+
logger.call(name, :warn, "execution timed out after #{timeout_interval} seconds")
|
78
|
+
else
|
79
|
+
logger.call(name, :info, 'execution completed successfully')
|
80
|
+
end
|
81
|
+
rescue Exception => ex
|
82
|
+
logger.call(name, :error, "execution failed with error '#{ex}'")
|
83
|
+
ensure
|
84
|
+
Thread.kill(worker)
|
85
|
+
worker = nil
|
86
|
+
end
|
87
|
+
break if Thread.current[:stop]
|
88
|
+
sleep(execution_interval)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
executor.abort_on_exception = false
|
93
|
+
return ExecutionContext.new(name, execution_interval, timeout_interval, executor)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|