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
data/lib/concurrent/functions.rb
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'concurrent/agent'
|
2
|
-
require 'concurrent/defer'
|
3
|
-
require 'concurrent/future'
|
4
|
-
require 'concurrent/promise'
|
5
|
-
|
6
|
-
module Kernel
|
7
|
-
|
8
|
-
## agent
|
9
|
-
|
10
|
-
def agent(initial, timeout = Concurrent::Agent::TIMEOUT)
|
11
|
-
return Concurrent::Agent.new(initial, timeout)
|
12
|
-
end
|
13
|
-
module_function :agent
|
14
|
-
|
15
|
-
def post(object, &block)
|
16
|
-
if object.respond_to?(:post)
|
17
|
-
return object.post(&block)
|
18
|
-
else
|
19
|
-
raise ArgumentError.new('object does not support #post')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
module_function :post
|
23
|
-
|
24
|
-
## defer
|
25
|
-
|
26
|
-
def defer(*args, &block)
|
27
|
-
return Concurrent::Defer.new(*args, &block)
|
28
|
-
end
|
29
|
-
module_function :defer
|
30
|
-
|
31
|
-
## executor
|
32
|
-
|
33
|
-
def executor(*args, &block)
|
34
|
-
return Concurrent::Executor.run(*args, &block)
|
35
|
-
end
|
36
|
-
module_function :executor
|
37
|
-
|
38
|
-
## future
|
39
|
-
|
40
|
-
def future(*args, &block)
|
41
|
-
return Concurrent::Future.new(*args, &block)
|
42
|
-
end
|
43
|
-
module_function :future
|
44
|
-
|
45
|
-
## obligation
|
46
|
-
|
47
|
-
def deref(object, timeout = nil)
|
48
|
-
if object.respond_to?(:deref)
|
49
|
-
return object.deref(timeout)
|
50
|
-
elsif object.respond_to?(:value)
|
51
|
-
return object.value(timeout)
|
52
|
-
else
|
53
|
-
raise ArgumentError.new('object does not support #deref')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
module_function :deref
|
57
|
-
|
58
|
-
def pending?(object)
|
59
|
-
if object.respond_to?(:pending?)
|
60
|
-
return object.pending?
|
61
|
-
else
|
62
|
-
raise ArgumentError.new('object does not support #pending?')
|
63
|
-
end
|
64
|
-
end
|
65
|
-
module_function :pending?
|
66
|
-
|
67
|
-
def fulfilled?(object)
|
68
|
-
if object.respond_to?(:fulfilled?)
|
69
|
-
return object.fulfilled?
|
70
|
-
elsif object.respond_to?(:realized?)
|
71
|
-
return object.realized?
|
72
|
-
else
|
73
|
-
raise ArgumentError.new('object does not support #fulfilled?')
|
74
|
-
end
|
75
|
-
end
|
76
|
-
module_function :fulfilled?
|
77
|
-
|
78
|
-
def realized?(object)
|
79
|
-
if object.respond_to?(:realized?)
|
80
|
-
return object.realized?
|
81
|
-
elsif object.respond_to?(:fulfilled?)
|
82
|
-
return object.fulfilled?
|
83
|
-
else
|
84
|
-
raise ArgumentError.new('object does not support #realized?')
|
85
|
-
end
|
86
|
-
end
|
87
|
-
module_function :realized?
|
88
|
-
|
89
|
-
def rejected?(object)
|
90
|
-
if object.respond_to?(:rejected?)
|
91
|
-
return object.rejected?
|
92
|
-
else
|
93
|
-
raise ArgumentError.new('object does not support #rejected?')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
module_function :rejected?
|
97
|
-
|
98
|
-
## promise
|
99
|
-
|
100
|
-
# Creates a new promise object. "A promise represents the eventual
|
101
|
-
# value returned from the single completion of an operation."
|
102
|
-
# Promises can be chained in a tree structure where each promise
|
103
|
-
# has zero or more children. Promises are resolved asynchronously
|
104
|
-
# in the order they are added to the tree. Parents are guaranteed
|
105
|
-
# to be resolved before their children. The result of each promise
|
106
|
-
# is passes to each of its children when the child resolves. When
|
107
|
-
# a promise is rejected all its children will be summarily rejected.
|
108
|
-
# A promise added to a rejected promise will immediately be rejected.
|
109
|
-
# A promise that is neither resolved or rejected is pending.
|
110
|
-
#
|
111
|
-
# @param args [Array] zero or more arguments for the block
|
112
|
-
# @param block [Proc] the block to call when attempting fulfillment
|
113
|
-
#
|
114
|
-
# @see Promise
|
115
|
-
# @see http://wiki.commonjs.org/wiki/Promises/A
|
116
|
-
def promise(*args, &block)
|
117
|
-
return Concurrent::Promise.new(*args, &block)
|
118
|
-
end
|
119
|
-
module_function :promise
|
120
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'concurrent/global_thread_pool'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
class NullThreadPool
|
6
|
-
behavior(:global_thread_pool)
|
7
|
-
|
8
|
-
def self.post(*args, &block)
|
9
|
-
Thread.new(*args, &block)
|
10
|
-
return true
|
11
|
-
end
|
12
|
-
|
13
|
-
def post(*args, &block)
|
14
|
-
return NullThreadPool.post(*args, &block)
|
15
|
-
end
|
16
|
-
|
17
|
-
def <<(block)
|
18
|
-
NullThreadPool.post(&block)
|
19
|
-
return self
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/lib/concurrent/reactor.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'functional'
|
3
|
-
|
4
|
-
behavior_info(:sync_event_demux,
|
5
|
-
start: 0,
|
6
|
-
stop: 0,
|
7
|
-
stopped?: 0,
|
8
|
-
accept: 0,
|
9
|
-
respond: 2,
|
10
|
-
close: 0)
|
11
|
-
|
12
|
-
behavior_info(:async_event_demux,
|
13
|
-
start: 0,
|
14
|
-
stop: 0,
|
15
|
-
stopped?: 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
|
-
|
27
|
-
RESERVED_EVENTS = [ :stop ]
|
28
|
-
|
29
|
-
EventContext = Struct.new(:event, :args, :callback)
|
30
|
-
|
31
|
-
def initialize(demux = nil)
|
32
|
-
@demux = demux
|
33
|
-
if @demux.nil? || @demux.behaves_as?(:async_event_demux)
|
34
|
-
@sync = false
|
35
|
-
@queue = Queue.new
|
36
|
-
@demux.set_reactor(self) unless @demux.nil?
|
37
|
-
elsif @demux.behaves_as?(:sync_event_demux)
|
38
|
-
@sync = true
|
39
|
-
else
|
40
|
-
raise ArgumentError.new("invalid event demultiplexer '#{@demux}'")
|
41
|
-
end
|
42
|
-
|
43
|
-
@running = false
|
44
|
-
@handlers = Hash.new
|
45
|
-
@mutex = Mutex.new
|
46
|
-
end
|
47
|
-
|
48
|
-
def running?
|
49
|
-
return @running
|
50
|
-
end
|
51
|
-
|
52
|
-
def add_handler(event, &block)
|
53
|
-
raise ArgumentError.new('no block given') unless block_given?
|
54
|
-
event = event.to_sym
|
55
|
-
raise ArgumentError.new("'#{event}' is a reserved event") if RESERVED_EVENTS.include?(event)
|
56
|
-
@mutex.synchronize {
|
57
|
-
@handlers[event] = block
|
58
|
-
}
|
59
|
-
return true
|
60
|
-
end
|
61
|
-
|
62
|
-
def remove_handler(event)
|
63
|
-
handler = @mutex.synchronize {
|
64
|
-
@handlers.delete(event.to_sym)
|
65
|
-
}
|
66
|
-
return ! handler.nil?
|
67
|
-
end
|
68
|
-
|
69
|
-
def stop_on_signal(*signals)
|
70
|
-
signals.each{|signal| Signal.trap(signal){ Thread.new{ self.stop }}}
|
71
|
-
end
|
72
|
-
|
73
|
-
def handle(event, *args)
|
74
|
-
raise NotImplementedError.new("demultiplexer '#{@demux.class}' is synchronous") if @sync
|
75
|
-
return [:stopped, 'reactor not running'] unless running?
|
76
|
-
context = EventContext.new(event.to_sym, args.dup, Queue.new)
|
77
|
-
@queue.push(context)
|
78
|
-
return context.callback.pop
|
79
|
-
end
|
80
|
-
|
81
|
-
def start
|
82
|
-
raise StandardError.new('already running') if self.running?
|
83
|
-
@sync ? (@running = true; run_sync) : (@running = true; run_async)
|
84
|
-
end
|
85
|
-
alias_method :run, :start
|
86
|
-
|
87
|
-
def stop
|
88
|
-
return true unless self.running?
|
89
|
-
if @sync
|
90
|
-
@demux.stop
|
91
|
-
else
|
92
|
-
@queue.push(:stop)
|
93
|
-
end
|
94
|
-
return true
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def handle_event(context)
|
100
|
-
raise ArgumentError.new('no block given') unless block_given?
|
101
|
-
|
102
|
-
handler = @mutex.synchronize {
|
103
|
-
@handlers[context.event]
|
104
|
-
}
|
105
|
-
|
106
|
-
if handler.nil?
|
107
|
-
response = yield(:noop, "'#{context.event}' handler not found")
|
108
|
-
else
|
109
|
-
begin
|
110
|
-
result = handler.call(*context.args)
|
111
|
-
response = yield(:ok, result)
|
112
|
-
rescue Exception => ex
|
113
|
-
response = yield(:ex, ex)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
return response
|
118
|
-
end
|
119
|
-
|
120
|
-
def finalize_stop
|
121
|
-
@mutex.synchronize do
|
122
|
-
@running = false
|
123
|
-
@demux.stop unless @demux.nil?
|
124
|
-
@demux = nil
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def run_sync
|
129
|
-
@demux.start
|
130
|
-
|
131
|
-
loop do
|
132
|
-
break if @demux.stopped?
|
133
|
-
context = @demux.accept
|
134
|
-
if context.nil?
|
135
|
-
@demux.close
|
136
|
-
else
|
137
|
-
response = handle_event(context) do |result, message|
|
138
|
-
[result, message]
|
139
|
-
end
|
140
|
-
@demux.respond(*response)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
finalize_stop
|
145
|
-
end
|
146
|
-
|
147
|
-
def run_async
|
148
|
-
@demux.start unless @demux.nil?
|
149
|
-
|
150
|
-
loop do
|
151
|
-
context = @queue.pop
|
152
|
-
break if context == :stop
|
153
|
-
handle_event(context) do |result, message|
|
154
|
-
context.callback.push([result, message])
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
finalize_stop
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'drb/drb'
|
2
|
-
require 'drb/acl'
|
3
|
-
require 'functional'
|
4
|
-
require 'concurrent/reactor'
|
5
|
-
|
6
|
-
module Concurrent
|
7
|
-
class Reactor
|
8
|
-
|
9
|
-
class DRbAsyncDemux
|
10
|
-
|
11
|
-
behavior(:async_event_demux)
|
12
|
-
|
13
|
-
DEFAULT_URI = 'druby://localhost:12345'
|
14
|
-
DEFAULT_ACL = %w[allow all]
|
15
|
-
|
16
|
-
def initialize(opts = {})
|
17
|
-
@uri = opts[:uri] || DEFAULT_URI
|
18
|
-
@acl = ACL.new(opts[:acl] || DEFAULT_ACL)
|
19
|
-
end
|
20
|
-
|
21
|
-
def set_reactor(reactor)
|
22
|
-
raise ArgumentError.new('invalid reactor') unless reactor.behaves_as?(:demux_reactor)
|
23
|
-
@reactor = reactor
|
24
|
-
end
|
25
|
-
|
26
|
-
def start
|
27
|
-
DRb.install_acl(@acl)
|
28
|
-
@service = DRb.start_service(@uri, Demultiplexer.new(@reactor))
|
29
|
-
end
|
30
|
-
|
31
|
-
def stop
|
32
|
-
@service = DRb.stop_service
|
33
|
-
end
|
34
|
-
|
35
|
-
def stopped?
|
36
|
-
return @service.nil?
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
class Demultiplexer
|
42
|
-
|
43
|
-
def initialize(reactor)
|
44
|
-
@reactor = reactor
|
45
|
-
end
|
46
|
-
|
47
|
-
Concurrent::Reactor::RESERVED_EVENTS.each do |event|
|
48
|
-
define_method(event){|*args| false }
|
49
|
-
end
|
50
|
-
|
51
|
-
def method_missing(method, *args, &block)
|
52
|
-
(class << self; self; end).class_eval do
|
53
|
-
define_method(method) do |*args|
|
54
|
-
result = @reactor.handle(method, *args)
|
55
|
-
case result.first
|
56
|
-
when :ok
|
57
|
-
return result.last
|
58
|
-
when :ex
|
59
|
-
raise result.last
|
60
|
-
when :noop
|
61
|
-
raise NoMethodError.new("undefined method '#{method}' for #{self}")
|
62
|
-
else
|
63
|
-
raise DRb::DRbUnknownError.new("unexpected error when calling method '#{method}'")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
self.send(method, *args)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
DRbAsyncDemultiplexer = DRbAsyncDemux
|
73
|
-
end
|
74
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'drb/acl'
|
3
|
-
require 'functional'
|
4
|
-
require 'concurrent/reactor'
|
5
|
-
|
6
|
-
module Concurrent
|
7
|
-
class Reactor
|
8
|
-
|
9
|
-
class TcpSyncDemux
|
10
|
-
|
11
|
-
behavior(:sync_event_demux)
|
12
|
-
|
13
|
-
DEFAULT_HOST = '127.0.0.1'
|
14
|
-
DEFAULT_PORT = 12345
|
15
|
-
DEFAULT_ACL = %[allow all]
|
16
|
-
|
17
|
-
def initialize(opts = {})
|
18
|
-
@host = opts[:host] || DEFAULT_HOST
|
19
|
-
@port = opts[:port] || DEFAULT_PORT
|
20
|
-
@acl = ACL.new(opts[:acl] || DEFAULT_ACL)
|
21
|
-
end
|
22
|
-
|
23
|
-
def start
|
24
|
-
@server = TCPServer.new(@host, @port)
|
25
|
-
end
|
26
|
-
|
27
|
-
def stop
|
28
|
-
atomic {
|
29
|
-
@socket.close unless @socket.nil?
|
30
|
-
@server.close unless @server.nil?
|
31
|
-
@server = @socket = nil
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
def stopped?
|
36
|
-
return @server.nil?
|
37
|
-
end
|
38
|
-
|
39
|
-
def accept
|
40
|
-
@socket = @server.accept if @socket.nil?
|
41
|
-
return nil unless @acl.allow_socket?(@socket)
|
42
|
-
event, args = get_message(@socket)
|
43
|
-
return nil if event.nil?
|
44
|
-
return Reactor::EventContext.new(event, args)
|
45
|
-
end
|
46
|
-
|
47
|
-
def respond(result, message)
|
48
|
-
return nil if @socket.nil?
|
49
|
-
@socket.puts(format_message(result, message))
|
50
|
-
end
|
51
|
-
|
52
|
-
def close
|
53
|
-
@socket.close
|
54
|
-
@socket = nil
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.format_message(event, *args)
|
58
|
-
args = args.reduce('') do |memo, arg|
|
59
|
-
memo << "#{arg}\r\n"
|
60
|
-
end
|
61
|
-
return ":#{event}\r\n#{args}\r\n"
|
62
|
-
end
|
63
|
-
def format_message(*args) self.class.format_message(*args); end
|
64
|
-
|
65
|
-
def self.parse_message(message)
|
66
|
-
return atomic {
|
67
|
-
event = message.first.match /^:?(\w+)/
|
68
|
-
event = event[1].to_s.downcase.to_sym unless event.nil?
|
69
|
-
|
70
|
-
args = message.slice(1, message.length) || []
|
71
|
-
|
72
|
-
[event, args]
|
73
|
-
}
|
74
|
-
end
|
75
|
-
def parse_message(*args) self.class.parse_message(*args); end
|
76
|
-
|
77
|
-
def self.get_message(socket)
|
78
|
-
message = []
|
79
|
-
while line = socket.gets
|
80
|
-
if line.nil? || (line = line.strip).empty?
|
81
|
-
break
|
82
|
-
else
|
83
|
-
message << line
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
if message.empty?
|
88
|
-
return nil
|
89
|
-
else
|
90
|
-
return parse_message(message)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
def get_message(*args) self.class.get_message(*args); end
|
94
|
-
end
|
95
|
-
|
96
|
-
TcpSyncDemultiplexer = TcpSyncDemux
|
97
|
-
end
|
98
|
-
end
|