rjr 0.15.1 → 0.16.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/bin/rjr-client +5 -3
- data/lib/rjr/common.rb +41 -8
- data/lib/rjr/dispatcher.rb +65 -23
- data/lib/rjr/em_adapter.rb +0 -3
- data/lib/rjr/{stats.rb → inspect.rb} +34 -9
- data/lib/rjr/message.rb +3 -2
- data/lib/rjr/node.rb +25 -10
- data/lib/rjr/nodes/amqp.rb +29 -22
- data/lib/rjr/nodes/local.rb +35 -16
- data/lib/rjr/nodes/multi.rb +4 -0
- data/lib/rjr/nodes/ssh.rb +1 -0
- data/lib/rjr/nodes/tcp.rb +8 -4
- data/lib/rjr/nodes/web.rb +7 -3
- data/lib/rjr/nodes/ws.rb +8 -4
- data/lib/rjr/thread_pool.rb +0 -4
- data/site/jrw.js +18 -13
- data/specs/dispatcher_spec.rb +8 -0
- data/specs/em_adapter_spec.rb +18 -24
- data/specs/{stats_spec.rb → inspect_spec.rb} +1 -1
- data/specs/node_spec.rb +5 -6
- data/specs/nodes/local_spec.rb +7 -1
- data/specs/thread_pool_spec.rb +15 -20
- metadata +5 -4
data/bin/rjr-client
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
require 'optparse'
|
8
8
|
require 'rjr/common'
|
9
|
-
require 'rjr/
|
9
|
+
require 'rjr/message'
|
10
10
|
require 'rjr/nodes/easy'
|
11
11
|
|
12
12
|
RJR::Logger.log_level = ::Logger::DEBUG
|
@@ -59,7 +59,7 @@ optparse = OptionParser.new do |opts|
|
|
59
59
|
config[:block] = !ret
|
60
60
|
end
|
61
61
|
|
62
|
-
opts.on('-n', '--num number_of_messages', 'Number of messages to send to server (may be :rand or indefinite)') do |n|
|
62
|
+
opts.on('-n', '--num number_of_messages', 'Number of messages to send to server (may be a number, :rand, or :indefinite)') do |n|
|
63
63
|
config[:num_msg] = case n.to_s.intern
|
64
64
|
when :rand then rand(MAX_MESSAGES)
|
65
65
|
when :indefinite then 1000000000 # XXX 'indefinite ;-)'
|
@@ -99,7 +99,9 @@ client_path = File.join(ENV['RJR_LOAD_PATH'] || File.join(cdir, '..', 'example
|
|
99
99
|
##########################################################
|
100
100
|
|
101
101
|
node = RJR::Nodes::Easy.new(NODES)
|
102
|
-
|
102
|
+
client_path.split(':').each { |cp|
|
103
|
+
node.dispatcher.add_modules(cp)
|
104
|
+
}
|
103
105
|
|
104
106
|
if config[:disconnect]
|
105
107
|
disconnect_thread = Thread.new {
|
data/lib/rjr/common.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
#
|
5
5
|
# Copyright (C) 2011-2013 Mohammed Morsi <mo@morsi.org>
|
6
6
|
# Licensed under the Apache License, Version 2.0
|
7
|
-
|
8
7
|
require 'logger'
|
9
8
|
|
10
9
|
# Return a random uuid
|
@@ -34,24 +33,51 @@ class Logger
|
|
34
33
|
@logger = ::Logger.new(output)
|
35
34
|
@logger.level = @log_level || ::Logger::FATAL
|
36
35
|
@logger_mutex = Mutex.new
|
36
|
+
@filters = []
|
37
|
+
@highlights = []
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
41
|
public
|
41
42
|
|
43
|
+
# Add method which to call on every log message to determine
|
44
|
+
# if messages should be included/excluded
|
45
|
+
def self.add_filter(filter)
|
46
|
+
@logger_mutex.synchronize{
|
47
|
+
@filters << filter
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a method which to call on every log message to determine
|
52
|
+
# if message should be highlighted
|
53
|
+
def self.highlight(hlight)
|
54
|
+
@logger_mutex.synchronize{
|
55
|
+
@highlights << hlight
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
42
59
|
def self.method_missing(method_id, *args)
|
43
60
|
_instantiate_logger
|
44
61
|
@logger_mutex.synchronize {
|
45
|
-
if args.first.is_a?(Array)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
|
62
|
+
args = args.first if args.first.is_a?(Array)
|
63
|
+
args.each { |a|
|
64
|
+
# run highlights / filters against output before
|
65
|
+
# sending formatted output to logger
|
66
|
+
# TODO allow user to customize highlight mechanism/text
|
67
|
+
na = @highlights.any? { |h| h.call a } ?
|
68
|
+
"\e[1m\e[31m#{a}\e[0m\e[0m" : a
|
69
|
+
@logger.send(method_id, na) if @filters.all? { |f| f.call a }
|
70
|
+
}
|
52
71
|
}
|
53
72
|
end
|
54
73
|
|
74
|
+
def self.safe_exec(*args, &bl)
|
75
|
+
_instantiate_logger
|
76
|
+
@logger_mutex.synchronize {
|
77
|
+
bl.call *args
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
55
81
|
def self.logger
|
56
82
|
_instantiate_logger
|
57
83
|
@logger
|
@@ -95,6 +121,13 @@ end
|
|
95
121
|
|
96
122
|
end # module RJR
|
97
123
|
|
124
|
+
# Serialized puts, uses logger lock to serialize puts output
|
125
|
+
def sputs(*args)
|
126
|
+
::RJR::Logger.safe_exec {
|
127
|
+
puts *args
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
98
131
|
class Object
|
99
132
|
def eigenclass
|
100
133
|
class << self
|
data/lib/rjr/dispatcher.rb
CHANGED
@@ -135,9 +135,16 @@ class Request
|
|
135
135
|
# Invoke the request by calling the registered handler with the registered
|
136
136
|
# method parameters in the local scope
|
137
137
|
def handle
|
138
|
-
|
138
|
+
node_sig = "#{@rjr_node_id}(#{@rjr_node_type})"
|
139
|
+
method_sig = "#{@rjr_method}(#{@rjr_method_args.join(',')})"
|
140
|
+
|
141
|
+
RJR::Logger.info "#{node_sig}->#{method_sig}"
|
142
|
+
|
139
143
|
retval = instance_exec(*@rjr_method_args, &@rjr_handler)
|
140
|
-
|
144
|
+
|
145
|
+
RJR::Logger.info \
|
146
|
+
"#{node_sig}<-#{method_sig}<-#{retval.nil? ? "nil" : retval}"
|
147
|
+
|
141
148
|
return retval
|
142
149
|
end
|
143
150
|
|
@@ -174,12 +181,16 @@ class Dispatcher
|
|
174
181
|
# Registered json-rpc request signatures and corresponding handlers
|
175
182
|
attr_reader :handlers
|
176
183
|
|
184
|
+
# Registered json-rpc request signatures and environments which to execute handlers in
|
185
|
+
attr_reader :environments
|
186
|
+
|
177
187
|
# Requests which have been dispatched
|
178
188
|
def requests ; @requests_lock.synchronize { Array.new(@requests) } ; end
|
179
189
|
|
180
190
|
# RJR::Dispatcher intializer
|
181
191
|
def initialize
|
182
192
|
@handlers = Hash.new()
|
193
|
+
@environments = Hash.new()
|
183
194
|
|
184
195
|
@requests_lock = Mutex.new
|
185
196
|
@requests = []
|
@@ -194,57 +205,88 @@ class Dispatcher
|
|
194
205
|
# a file, directory, or path specification (dirs seperated with ':')
|
195
206
|
# @return self
|
196
207
|
def add_module(name)
|
197
|
-
name
|
198
|
-
if File.directory?(p)
|
199
|
-
# TODO also .so files? allow user to specify suffix
|
200
|
-
Dir.glob(File.join(p, '*.rb')).all? { |m|
|
201
|
-
require m
|
202
|
-
}
|
208
|
+
require name
|
203
209
|
|
204
|
-
|
205
|
-
|
210
|
+
m = name.downcase.gsub(File::SEPARATOR, '_')
|
211
|
+
method("dispatch_#{m}".intern).call(self)
|
206
212
|
|
207
|
-
end
|
208
|
-
|
209
|
-
m = p.split(File::SEPARATOR).last
|
210
|
-
method("dispatch_#{m}".intern).call(self)
|
211
|
-
}
|
212
213
|
self
|
213
214
|
end
|
214
215
|
alias :add_modules :add_module
|
215
216
|
|
216
217
|
# Register json-rpc handler with dispatcher
|
217
218
|
#
|
218
|
-
# @param [String] signature request signature to match
|
219
|
+
# @param [String,Regex] signature request signature to match
|
219
220
|
# @param [Callable] callable callable object which to bind to signature
|
220
221
|
# @param [Callable] &bl block parameter will be set to callback if specified
|
221
222
|
# @return self
|
222
223
|
def handle(signature, callback = nil, &bl)
|
223
224
|
if signature.is_a?(Array)
|
224
225
|
signature.each { |s| handle(s, callback, &bl) }
|
225
|
-
return
|
226
|
+
return self
|
226
227
|
end
|
227
228
|
@handlers[signature] = callback unless callback.nil?
|
228
229
|
@handlers[signature] = bl unless bl.nil?
|
229
230
|
self
|
230
231
|
end
|
231
232
|
|
233
|
+
# Return boolean indicating if dispatcher can handle method
|
234
|
+
#
|
235
|
+
# @param [String] string rjr method to match
|
236
|
+
# @return [true,false] indicating if requests to specified method will be matched
|
237
|
+
def handles?(rjr_method)
|
238
|
+
!@handlers.find { |k,v|
|
239
|
+
k.is_a?(String) ?
|
240
|
+
k == rjr_method :
|
241
|
+
k =~ rjr_method
|
242
|
+
}.nil?
|
243
|
+
end
|
244
|
+
|
245
|
+
# Register environment to run json-rpc handler w/ dispatcher.
|
246
|
+
#
|
247
|
+
# Currently environments may be set to modules which requests
|
248
|
+
# will extend before executing handler
|
249
|
+
#
|
250
|
+
# @param [String,Regex] signature request signature to match
|
251
|
+
# @param [Module] module which to extend requests with
|
252
|
+
# @return self
|
253
|
+
def env(signature, environment)
|
254
|
+
if signature.is_a?(Array)
|
255
|
+
signature.each { |s| env(s, environment) }
|
256
|
+
return self
|
257
|
+
end
|
258
|
+
@environments[signature] = environment
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
232
262
|
# Dispatch received request. (used internally by nodes).
|
233
263
|
#
|
234
264
|
# Arguments should include :rjr_method and other parameters
|
235
265
|
# required to construct a valid Request instance
|
236
266
|
def dispatch(args = {})
|
237
|
-
# currently we
|
238
|
-
# TODO
|
239
|
-
# or if callable, invoke it w/ request checking boolean return value for match
|
240
|
-
# TODO not using concurrent access portection, assumes all handlers are registered
|
267
|
+
# currently we match method name string or regex against signature
|
268
|
+
# TODO not using concurrent access protection, assumes all handlers are registered
|
241
269
|
# before first dispatch occurs
|
242
|
-
handler = @handlers
|
270
|
+
handler = @handlers.find { |k,v|
|
271
|
+
k.is_a?(String) ?
|
272
|
+
k == args[:rjr_method] :
|
273
|
+
k =~ args[:rjr_method] }
|
274
|
+
|
275
|
+
# TODO currently just using last environment that matches,
|
276
|
+
# allow multiple environments to be used?
|
277
|
+
environment = @environments.keys.select { |k|
|
278
|
+
k.is_a?(String) ?
|
279
|
+
k == args[:rjr_method] :
|
280
|
+
k =~ args[:rjr_method]
|
281
|
+
}.last
|
243
282
|
|
244
283
|
return Result.method_not_found(args[:rjr_method]) if handler.nil?
|
245
284
|
|
246
285
|
# TODO compare arity of handler to number of method_args passed in
|
247
|
-
request = Request.new args.merge(:rjr_handler => handler)
|
286
|
+
request = Request.new args.merge(:rjr_handler => handler.last)
|
287
|
+
|
288
|
+
# set request environment
|
289
|
+
request.extend(@environments[environment]) unless environment.nil?
|
248
290
|
|
249
291
|
begin
|
250
292
|
retval = request.handle
|
data/lib/rjr/em_adapter.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
require 'singleton'
|
7
6
|
require 'eventmachine'
|
8
7
|
|
9
8
|
module RJR
|
@@ -11,8 +10,6 @@ module RJR
|
|
11
10
|
# EventMachine adapater interface, ties reactor
|
12
11
|
# lifecycle to an instance of this class.
|
13
12
|
class EMAdapter
|
14
|
-
include Singleton
|
15
|
-
|
16
13
|
# Run reactor in its own interally managed thread
|
17
14
|
attr_accessor :reactor_thread
|
18
15
|
|
@@ -8,6 +8,14 @@
|
|
8
8
|
# Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
|
9
9
|
# Licensed under the Apache License, Version 2.0
|
10
10
|
|
11
|
+
# TODO
|
12
|
+
# - data received/sent (on different interfaces)
|
13
|
+
# - messages received/sent (on different interfaces)
|
14
|
+
# - dispatches on a per node basis
|
15
|
+
# - unresolved/invalid dispatches/messages
|
16
|
+
# - em jobs
|
17
|
+
# - thread pool jobs (started / completed / etc)
|
18
|
+
|
11
19
|
require 'eventmachine'
|
12
20
|
|
13
21
|
# Helper method to process user params / select stats
|
@@ -37,34 +45,51 @@ def select_stats(dispatcher, *filter)
|
|
37
45
|
dispatcher.requests.select { |ds| lf.all? { |lfi| lfi.call(ds) } }
|
38
46
|
end
|
39
47
|
|
40
|
-
# Add
|
41
|
-
def
|
48
|
+
# Add inspection methods to specified dispatcher
|
49
|
+
def dispatch_rjr_inspect(dispatcher)
|
42
50
|
# Retrieve all the dispatches this node served matching the specified criteri
|
43
51
|
dispatcher.handle "rjr::dispatches" do |filter|
|
44
|
-
select_stats(*filter)
|
52
|
+
select_stats(dispatcher, *filter)
|
45
53
|
end
|
46
54
|
|
47
55
|
# Retrieve the number of dispatches this node served matching the specified criteria
|
48
56
|
dispatcher.handle "rjr::num_dispatches" do |filter|
|
49
|
-
select_stats(*filter).size
|
57
|
+
select_stats(dispatcher, *filter).size
|
50
58
|
end
|
51
59
|
|
52
60
|
# Retrieve the internal status of this node
|
53
61
|
dispatcher.handle "rjr::status" do
|
62
|
+
nodes = []
|
63
|
+
ObjectSpace.each_object RJR::Node do |node|
|
64
|
+
nodes << node.to_s
|
65
|
+
end
|
66
|
+
|
54
67
|
{
|
68
|
+
# nodes
|
69
|
+
:nodes => nodes,
|
70
|
+
|
71
|
+
# dispatcher
|
72
|
+
:dispatcher => {
|
73
|
+
:requests => dispatcher.requests.size,
|
74
|
+
:handlers =>
|
75
|
+
dispatcher.handlers.keys,
|
76
|
+
#dispatcher.handlers.collect { |k,v|
|
77
|
+
# [k, v.source_location] },
|
78
|
+
:environments => dispatcher.environments
|
79
|
+
},
|
80
|
+
|
55
81
|
# event machine
|
56
82
|
:event_machine => { :running => EventMachine.reactor_running?,
|
57
|
-
:thread_status =>
|
58
|
-
|
59
|
-
|
83
|
+
:thread_status =>
|
84
|
+
(RJR::Node.em && RJR::Node.em.reactor_thread) ?
|
85
|
+
RJR::Node.em.reactor_thread.status : nil,
|
60
86
|
:connections => EventMachine.connection_count },
|
61
87
|
|
62
88
|
# thread pool
|
63
|
-
:thread_pool => { :running => RJR::
|
89
|
+
:thread_pool => { :running => RJR::Node.tp ? RJR::Node.tp.running? : nil }
|
64
90
|
}
|
65
91
|
end
|
66
92
|
|
67
93
|
#:log =>
|
68
94
|
# lambda {},
|
69
95
|
end
|
70
|
-
|
data/lib/rjr/message.rb
CHANGED
@@ -5,8 +5,9 @@
|
|
5
5
|
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
6
6
|
# Licensed under the Apache License, Version 2.0
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# FIXME https://github.com/flori/json/issues/179
|
9
|
+
# if pull request doesn't get accepted implement
|
10
|
+
# one of the workarounds in rjr
|
10
11
|
|
11
12
|
require 'json'
|
12
13
|
require 'rjr/common'
|
data/lib/rjr/node.rb
CHANGED
@@ -44,6 +44,15 @@ class Node
|
|
44
44
|
# Dispatcher to use to satisfy requests
|
45
45
|
attr_accessor :dispatcher
|
46
46
|
|
47
|
+
# alias of RJR_NODE_TYPE
|
48
|
+
def node_type
|
49
|
+
self.class::RJR_NODE_TYPE
|
50
|
+
end
|
51
|
+
|
52
|
+
# XXX used by debugging / stats interface
|
53
|
+
def self.em ; defined?(@@em) ? @@em : nil end
|
54
|
+
def self.tp ; defined?(@@tp) ? @@tp : nil end
|
55
|
+
|
47
56
|
# RJR::Node initializer
|
48
57
|
#
|
49
58
|
# @param [Hash] args options to set on request
|
@@ -60,16 +69,20 @@ class Node
|
|
60
69
|
@dispatcher = args[:dispatcher] || RJR::Dispatcher.new
|
61
70
|
@message_headers = args.has_key?(:headers) ? {}.merge(args[:headers]) : {}
|
62
71
|
|
63
|
-
|
64
|
-
|
72
|
+
@@tp ||= ThreadPool.new
|
73
|
+
@@em ||= EMAdapter.new
|
74
|
+
|
75
|
+
# will do nothing if already started
|
76
|
+
@@tp.start
|
77
|
+
@@em.start
|
65
78
|
end
|
66
79
|
|
67
80
|
# Block until the eventmachine reactor and thread pool have both completed running
|
68
81
|
#
|
69
82
|
# @return self
|
70
83
|
def join
|
71
|
-
|
72
|
-
|
84
|
+
@@tp.join
|
85
|
+
@@em.join
|
73
86
|
self
|
74
87
|
end
|
75
88
|
|
@@ -80,8 +93,8 @@ class Node
|
|
80
93
|
#
|
81
94
|
# @return self
|
82
95
|
def halt
|
83
|
-
|
84
|
-
|
96
|
+
@@em.stop_event_loop
|
97
|
+
@@tp.stop
|
85
98
|
self
|
86
99
|
end
|
87
100
|
|
@@ -113,10 +126,10 @@ class Node
|
|
113
126
|
# Internal helper, handle message received
|
114
127
|
def handle_message(msg, connection = {})
|
115
128
|
if RequestMessage.is_request_message?(msg)
|
116
|
-
|
129
|
+
@@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
|
117
130
|
|
118
131
|
elsif NotificationMessage.is_notification_message?(msg)
|
119
|
-
|
132
|
+
@@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
|
120
133
|
|
121
134
|
elsif ResponseMessage.is_response_message?(msg)
|
122
135
|
handle_response(msg)
|
@@ -142,12 +155,12 @@ class Node
|
|
142
155
|
NotificationMessage.new(:message => data,
|
143
156
|
:headers => @message_headers) :
|
144
157
|
RequestMessage.new(:message => data,
|
145
|
-
|
158
|
+
:headers => @message_headers)
|
146
159
|
|
147
160
|
result =
|
148
161
|
@dispatcher.dispatch(:rjr_method => msg.jr_method,
|
149
162
|
:rjr_method_args => msg.jr_args,
|
150
|
-
:
|
163
|
+
:rjr_headers => msg.headers,
|
151
164
|
:rjr_client_ip => client_ip,
|
152
165
|
:rjr_client_port => client_port,
|
153
166
|
:rjr_node => self,
|
@@ -164,6 +177,8 @@ class Node
|
|
164
177
|
self.send_msg(response.to_s, connection)
|
165
178
|
return response
|
166
179
|
end
|
180
|
+
|
181
|
+
nil
|
167
182
|
end
|
168
183
|
|
169
184
|
# Internal helper, handle response message received
|
data/lib/rjr/nodes/amqp.rb
CHANGED
@@ -61,30 +61,33 @@ class AMQP < RJR::Node
|
|
61
61
|
return
|
62
62
|
end
|
63
63
|
|
64
|
-
@conn = ::AMQP.connect(:host => @broker) do
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
@conn = ::AMQP.connect(:host => @broker) do |conn|
|
65
|
+
::AMQP.connection = conn # XXX not sure why this is needed but the amqp
|
66
|
+
# em interface won't shut down cleanly otherwise
|
67
|
+
|
68
|
+
conn.on_tcp_connection_failure { puts "OTCF #{@node_id}" }
|
68
69
|
|
69
|
-
|
70
|
+
### connect to qpid broker
|
71
|
+
@channel = ::AMQP::Channel.new(conn)
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
+
# qpid constructs that will be created for node
|
74
|
+
@queue_name = "#{@node_id.to_s}-queue"
|
75
|
+
@queue = @channel.queue(@queue_name, :auto_delete => true)
|
76
|
+
@exchange = @channel.default_exchange
|
73
77
|
|
74
|
-
|
75
|
-
|
76
|
-
@queue = @channel.queue(@queue_name, :auto_delete => true)
|
77
|
-
@exchange = @channel.default_exchange
|
78
|
+
@listening = false
|
79
|
+
#@disconnected = false
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
+
@exchange.on_return do |basic_return, metadata, payload|
|
82
|
+
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
83
|
+
#@disconnected = true # FIXME member will be set on wrong class
|
84
|
+
# TODO these are only run when we fail to send message to queue,
|
85
|
+
# need to detect when that queue is shutdown & other events
|
86
|
+
connection_event(:error)
|
87
|
+
connection_event(:closed)
|
88
|
+
end
|
81
89
|
|
82
|
-
|
83
|
-
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
84
|
-
#@disconnected = true # FIXME member will be set on wrong class
|
85
|
-
# TODO these are only run when we fail to send message to queue, need to detect when that queue is shutdown & other events
|
86
|
-
connection_event(:error)
|
87
|
-
connection_event(:closed)
|
90
|
+
on_init.call
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
@@ -116,6 +119,10 @@ class AMQP < RJR::Node
|
|
116
119
|
@amqp_lock = Mutex.new
|
117
120
|
end
|
118
121
|
|
122
|
+
def to_s
|
123
|
+
"RJR::Nodes::AMQP<#{@node_id},#{@broker},#{@queue_name}>"
|
124
|
+
end
|
125
|
+
|
119
126
|
# Publish a message using the amqp exchange
|
120
127
|
#
|
121
128
|
# Implementation of {RJR::Node#send_msg}
|
@@ -137,7 +144,7 @@ class AMQP < RJR::Node
|
|
137
144
|
#
|
138
145
|
# Implementation of {RJR::Node#listen}
|
139
146
|
def listen
|
140
|
-
|
147
|
+
@@em.schedule do
|
141
148
|
init_node {
|
142
149
|
subscribe # start receiving messages
|
143
150
|
}
|
@@ -161,7 +168,7 @@ class AMQP < RJR::Node
|
|
161
168
|
message = RequestMessage.new :method => rpc_method,
|
162
169
|
:args => args,
|
163
170
|
:headers => @message_headers
|
164
|
-
|
171
|
+
@@em.schedule do
|
165
172
|
init_node {
|
166
173
|
subscribe # begin listening for result
|
167
174
|
send_msg(message.to_s, :routing_key => routing_key, :reply_to => @queue_name)
|
@@ -197,7 +204,7 @@ class AMQP < RJR::Node
|
|
197
204
|
message = NotificationMessage.new :method => rpc_method,
|
198
205
|
:args => args,
|
199
206
|
:headers => @message_headers
|
200
|
-
|
207
|
+
@@em.schedule do
|
201
208
|
init_node {
|
202
209
|
send_msg(message.to_s, :routing_key => routing_key, :reply_to => @queue_name){
|
203
210
|
published_l.synchronize { invoked = true ; published_c.signal }
|
data/lib/rjr/nodes/local.rb
CHANGED
@@ -26,7 +26,7 @@ module Nodes
|
|
26
26
|
#
|
27
27
|
# @example Listening for and dispatching json-rpc requests locally
|
28
28
|
# # initialize node
|
29
|
-
# node = RJR::
|
29
|
+
# node = RJR::Nodes::Local.new :node_id => 'node'
|
30
30
|
#
|
31
31
|
# node.dispatcher.handle('hello') do |name|
|
32
32
|
# @rjr_node_type == :local ? "Hello superuser #{name}" : "Hello #{name}!"
|
@@ -41,13 +41,17 @@ class Local < RJR::Node
|
|
41
41
|
# allows clients to override the node type for the local node
|
42
42
|
attr_accessor :node_type
|
43
43
|
|
44
|
-
#
|
44
|
+
# Nodes::Local initializer
|
45
45
|
# @param [Hash] args the options to create the local node with
|
46
46
|
def initialize(args = {})
|
47
47
|
super(args)
|
48
48
|
@node_type = RJR_NODE_TYPE
|
49
49
|
end
|
50
50
|
|
51
|
+
def to_s
|
52
|
+
"RJR::Nodes::Local<#{@node_id}>"
|
53
|
+
end
|
54
|
+
|
51
55
|
# Send data using specified connection.
|
52
56
|
#
|
53
57
|
# Simply dispatch local notification.
|
@@ -55,8 +59,8 @@ class Local < RJR::Node
|
|
55
59
|
# Implementation of {RJR::Node#send_msg}
|
56
60
|
def send_msg(msg, connection)
|
57
61
|
# ignore response message
|
58
|
-
unless
|
59
|
-
|
62
|
+
unless ResponseMessage.is_response_message?(msg)
|
63
|
+
launch_request(msg, true) # .join?
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
@@ -68,6 +72,18 @@ class Local < RJR::Node
|
|
68
72
|
self
|
69
73
|
end
|
70
74
|
|
75
|
+
# Helper to launch request in new thread
|
76
|
+
#
|
77
|
+
# This needs to happen so that each request runs in its own context
|
78
|
+
# (or close to it, globals will still be available, but locks will
|
79
|
+
# not be locally held, etc)
|
80
|
+
def launch_request(req, notification)
|
81
|
+
Thread.new(req,notification) { |req,notification|
|
82
|
+
res = handle_request(req, notification, nil)
|
83
|
+
handle_response(res.to_s) unless res.nil?
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
71
87
|
# Instructs node to send rpc request, and wait for and return response
|
72
88
|
#
|
73
89
|
# Implementation of {RJR::Node#invoke}
|
@@ -85,9 +101,16 @@ class Local < RJR::Node
|
|
85
101
|
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
86
102
|
message = RequestMessage.new(:method => rpc_method,
|
87
103
|
:args => args,
|
88
|
-
:headers => @message_headers)
|
89
|
-
|
90
|
-
|
104
|
+
:headers => @message_headers)
|
105
|
+
launch_request(message.to_s, false)
|
106
|
+
|
107
|
+
# TODO optional timeout for response ?
|
108
|
+
res = wait_for_result(message)
|
109
|
+
|
110
|
+
if res.size > 2
|
111
|
+
raise Exception, res[2]
|
112
|
+
end
|
113
|
+
return res[1]
|
91
114
|
end
|
92
115
|
|
93
116
|
# Instructs node to send rpc notification (immediately returns / no response is generated)
|
@@ -99,15 +122,11 @@ class Local < RJR::Node
|
|
99
122
|
# @param [String] rpc_method json-rpc method to invoke on destination
|
100
123
|
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
101
124
|
def notify(rpc_method, *args)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
:headers => @message_headers).to_s
|
108
|
-
handle_request(message, true, nil)
|
109
|
-
rescue
|
110
|
-
end
|
125
|
+
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
126
|
+
message = NotificationMessage.new(:method => rpc_method,
|
127
|
+
:args => args,
|
128
|
+
:headers => @message_headers)
|
129
|
+
launch_request(message.to_s, true) #.join ?
|
111
130
|
nil
|
112
131
|
end
|
113
132
|
|
data/lib/rjr/nodes/multi.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
data/lib/rjr/nodes/tcp.rb
CHANGED
@@ -116,6 +116,10 @@ class TCP < RJR::Node
|
|
116
116
|
@connections_lock = Mutex.new
|
117
117
|
end
|
118
118
|
|
119
|
+
def to_s
|
120
|
+
"RJR::Nodes::TCP<#{@node_id},#{@host},#{@port}>"
|
121
|
+
end
|
122
|
+
|
119
123
|
# Send data using specified connection
|
120
124
|
#
|
121
125
|
# Implementation of {RJR::Node#send_msg}
|
@@ -127,8 +131,8 @@ class TCP < RJR::Node
|
|
127
131
|
#
|
128
132
|
# Implementation of {RJR::Node#listen}
|
129
133
|
def listen
|
130
|
-
|
131
|
-
|
134
|
+
@@em.schedule {
|
135
|
+
@@em.start_server @host, @port, TCPConnection, { :rjr_node => self }
|
132
136
|
}
|
133
137
|
self
|
134
138
|
end
|
@@ -152,7 +156,7 @@ class TCP < RJR::Node
|
|
152
156
|
:args => args,
|
153
157
|
:headers => @message_headers
|
154
158
|
connection = nil
|
155
|
-
|
159
|
+
@@em.schedule {
|
156
160
|
init_client(:host => host, :port => port,
|
157
161
|
:rjr_node => self) { |c|
|
158
162
|
connection = c
|
@@ -190,7 +194,7 @@ class TCP < RJR::Node
|
|
190
194
|
message = NotificationMessage.new :method => rpc_method,
|
191
195
|
:args => args,
|
192
196
|
:headers => @message_headers
|
193
|
-
|
197
|
+
@@em.schedule {
|
194
198
|
init_client(:host => host, :port => port,
|
195
199
|
:rjr_node => self) { |c|
|
196
200
|
conn = c
|
data/lib/rjr/nodes/web.rb
CHANGED
@@ -103,6 +103,10 @@ class Web < RJR::Node
|
|
103
103
|
@port = args[:port]
|
104
104
|
end
|
105
105
|
|
106
|
+
def to_s
|
107
|
+
"RJR::Nodes::Web<#{@node_id},#{@host},#{@port}>"
|
108
|
+
end
|
109
|
+
|
106
110
|
# Send data using specified http connection
|
107
111
|
#
|
108
112
|
# Implementation of {RJR::Node#send_msg}
|
@@ -123,7 +127,7 @@ class Web < RJR::Node
|
|
123
127
|
#
|
124
128
|
# Implementation of {RJR::Node#listen}
|
125
129
|
def listen
|
126
|
-
|
130
|
+
@@em.schedule do
|
127
131
|
EventMachine::start_server(@host, @port, WebConnection, :rjr_node => self)
|
128
132
|
end
|
129
133
|
self
|
@@ -149,7 +153,7 @@ class Web < RJR::Node
|
|
149
153
|
handle_message(http.response, http)
|
150
154
|
}
|
151
155
|
|
152
|
-
|
156
|
+
@@em.schedule do
|
153
157
|
http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
|
154
158
|
http.errback &cb
|
155
159
|
http.callback &cb
|
@@ -182,7 +186,7 @@ class Web < RJR::Node
|
|
182
186
|
:args => args,
|
183
187
|
:headers => @message_headers
|
184
188
|
cb = lambda { |arg| published_l.synchronize { invoked = true ; published_c.signal }}
|
185
|
-
|
189
|
+
@@em.schedule do
|
186
190
|
http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
|
187
191
|
http.errback &cb
|
188
192
|
http.callback &cb
|
data/lib/rjr/nodes/ws.rb
CHANGED
@@ -93,6 +93,10 @@ class WS < RJR::Node
|
|
93
93
|
@connections_lock = Mutex.new
|
94
94
|
end
|
95
95
|
|
96
|
+
def to_s
|
97
|
+
"RJR::Nodes::WS<#{@node_id},#{@host},#{@port}>"
|
98
|
+
end
|
99
|
+
|
96
100
|
# Send data using specified websocket safely
|
97
101
|
#
|
98
102
|
# Implementation of {RJR::Node#send_msg}
|
@@ -104,8 +108,8 @@ class WS < RJR::Node
|
|
104
108
|
#
|
105
109
|
# Implementation of {RJR::Node#listen}
|
106
110
|
def listen
|
107
|
-
|
108
|
-
EventMachine::WebSocket.
|
111
|
+
@@em.schedule do
|
112
|
+
EventMachine::WebSocket.run(:host => @host, :port => @port) do |ws|
|
109
113
|
ws.onopen { }
|
110
114
|
ws.onclose { @connection_event_handlers[:closed].each { |h| h.call self } }
|
111
115
|
ws.onerror { |e| @connection_event_handlers[:error].each { |h| h.call self } }
|
@@ -131,7 +135,7 @@ class WS < RJR::Node
|
|
131
135
|
:args => args,
|
132
136
|
:headers => @message_headers
|
133
137
|
|
134
|
-
|
138
|
+
@@em.schedule {
|
135
139
|
init_client(uri) do |c|
|
136
140
|
c.stream { |msg| handle_message(msg, c) }
|
137
141
|
|
@@ -165,7 +169,7 @@ class WS < RJR::Node
|
|
165
169
|
message = NotificationMessage.new :method => rpc_method,
|
166
170
|
:args => args,
|
167
171
|
:headers => @message_headers
|
168
|
-
|
172
|
+
@@em.schedule {
|
169
173
|
init_client(uri) do |c|
|
170
174
|
c.stream { |msg| handle_message(msg, c) }
|
171
175
|
|
data/lib/rjr/thread_pool.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
# Copyright (C) 2010-2013 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
require 'singleton'
|
7
|
-
|
8
6
|
module RJR
|
9
7
|
|
10
8
|
# Work item to be executed in a thread launched by {ThreadPool}.
|
@@ -81,8 +79,6 @@ end
|
|
81
79
|
# Supports optional timeout which allows the user to kill and restart
|
82
80
|
# threads if a job is taking too long to run.
|
83
81
|
class ThreadPool
|
84
|
-
include Singleton
|
85
|
-
|
86
82
|
class << self
|
87
83
|
# @!group Config options (must be set before first node is instantiated)
|
88
84
|
|
data/site/jrw.js
CHANGED
@@ -120,7 +120,7 @@ function JRObject (type, value, ignore_properties){
|
|
120
120
|
this.type = type;
|
121
121
|
this.value = value;
|
122
122
|
this.ignore_properties = (typeof(ignore_properties) != 'undefined') ?
|
123
|
-
ignore_properties : ["toJSON"];
|
123
|
+
ignore_properties : ["toJSON", "json_class"];
|
124
124
|
this.toJSON = function(){
|
125
125
|
var data = {};
|
126
126
|
for(p in this.value)
|
@@ -169,6 +169,7 @@ JRObject.from_json_array = function(json){
|
|
169
169
|
// Main json-rpc client websocket interface
|
170
170
|
function WSNode (host, port){
|
171
171
|
var node = this;
|
172
|
+
this.opening = false;
|
172
173
|
this.opened = false;
|
173
174
|
this.node_id = null;
|
174
175
|
this.headers = {};
|
@@ -176,6 +177,8 @@ function WSNode (host, port){
|
|
176
177
|
|
177
178
|
// Open socket connection
|
178
179
|
this.open = function(){
|
180
|
+
if(this.opening) return;
|
181
|
+
this.opening = true;
|
179
182
|
node.socket = new WebSocket("ws://" + host + ":" + port);
|
180
183
|
|
181
184
|
node.socket.onclose = function (){
|
@@ -197,14 +200,14 @@ function WSNode (host, port){
|
|
197
200
|
if(node.onerror)
|
198
201
|
node.onerror(msg)
|
199
202
|
|
200
|
-
}else{
|
201
|
-
// relying on clients to handle notifications via message_received
|
202
|
-
// TODO add notification (and request?) handler support here
|
203
|
-
//node.invoke_method(msg.rpc_method, msg.params)
|
204
|
-
if(node.message_received)
|
205
|
-
node.message_received(msg);
|
206
|
-
|
207
203
|
}
|
204
|
+
|
205
|
+
// relying on clients to handle notifications via message_received
|
206
|
+
// TODO add notification (and request?) handler support here
|
207
|
+
// clients may user this to register additional handlers to be invoked
|
208
|
+
// upon request responses
|
209
|
+
if(node.message_received)
|
210
|
+
node.message_received(msg);
|
208
211
|
};
|
209
212
|
|
210
213
|
node.socket.onerror = function(e){
|
@@ -213,12 +216,13 @@ function WSNode (host, port){
|
|
213
216
|
}
|
214
217
|
|
215
218
|
node.socket.onopen = function (){
|
219
|
+
node.opened = true;
|
220
|
+
node.opening = false;
|
221
|
+
|
216
222
|
// send queued messages
|
217
223
|
for(var m in node.messages)
|
218
224
|
node.socket.send(node.messages[m].to_json());
|
219
225
|
|
220
|
-
node.opened = true;
|
221
|
-
|
222
226
|
// invoke client callback
|
223
227
|
if(node.onopen)
|
224
228
|
node.onopen();
|
@@ -270,9 +274,10 @@ function WebNode (uri){
|
|
270
274
|
|
271
275
|
success: function(data){
|
272
276
|
var msg = JRMessage.from_msg(data);
|
273
|
-
//
|
274
|
-
//
|
275
|
-
|
277
|
+
// clients may register additional callbacks
|
278
|
+
// to handle web node responses
|
279
|
+
if(node.message_received)
|
280
|
+
node.message_received(msg);
|
276
281
|
|
277
282
|
req.handle_response(msg)
|
278
283
|
|
data/specs/dispatcher_spec.rb
CHANGED
@@ -145,6 +145,7 @@ module RJR
|
|
145
145
|
h = proc {}
|
146
146
|
d.handle('foobar', h)
|
147
147
|
d.handlers['foobar'].should == h
|
148
|
+
d.handles?('foobar').should be_true
|
148
149
|
end
|
149
150
|
|
150
151
|
it "should set handler from block param" do
|
@@ -152,6 +153,7 @@ module RJR
|
|
152
153
|
h = proc {}
|
153
154
|
d.handle('foobar', &h)
|
154
155
|
d.handlers['foobar'].should == h
|
156
|
+
d.handles?('foobar').should be_true
|
155
157
|
end
|
156
158
|
|
157
159
|
it "should register handler for multiple methods" do
|
@@ -160,6 +162,8 @@ module RJR
|
|
160
162
|
d.handle(['foobar', 'barfoo'], &h)
|
161
163
|
d.handlers['foobar'].should == h
|
162
164
|
d.handlers['barfoo'].should == h
|
165
|
+
d.handles?('foobar').should be_true
|
166
|
+
d.handles?('barfoo').should be_true
|
163
167
|
end
|
164
168
|
end
|
165
169
|
|
@@ -183,6 +187,10 @@ module RJR
|
|
183
187
|
invoked.should be_true
|
184
188
|
end
|
185
189
|
|
190
|
+
context "handler is regex" do
|
191
|
+
it "should match method"
|
192
|
+
end
|
193
|
+
|
186
194
|
it "should pass params to handler" do
|
187
195
|
param = nil
|
188
196
|
d = Dispatcher.new
|
data/specs/em_adapter_spec.rb
CHANGED
@@ -3,49 +3,43 @@ require 'rjr/em_adapter'
|
|
3
3
|
|
4
4
|
module RJR
|
5
5
|
describe EMAdapter do
|
6
|
-
|
7
|
-
EMAdapter.
|
8
|
-
EMAdapter.instance.join
|
6
|
+
before(:each) do
|
7
|
+
@em = EMAdapter.new
|
9
8
|
end
|
10
9
|
|
11
|
-
|
12
|
-
em
|
13
|
-
EMAdapter.instance.should == em
|
10
|
+
after(:each) do
|
11
|
+
@em.halt.join
|
14
12
|
end
|
15
13
|
|
16
14
|
it "should start the reactor" do
|
17
|
-
em
|
18
|
-
em.
|
19
|
-
|
20
|
-
['sleep', 'run'].should include(em.reactor_thread.status)
|
15
|
+
@em.start
|
16
|
+
@em.reactor_running?.should be_true
|
17
|
+
['sleep', 'run'].should include(@em.reactor_thread.status)
|
21
18
|
end
|
22
19
|
|
23
20
|
it "should only start the reactor once" do
|
24
|
-
em
|
25
|
-
em.start
|
21
|
+
@em.start
|
26
22
|
|
27
|
-
ot = em.reactor_thread
|
28
|
-
em.start
|
29
|
-
em.reactor_thread.should == ot
|
23
|
+
ot = @em.reactor_thread
|
24
|
+
@em.start
|
25
|
+
@em.reactor_thread.should == ot
|
30
26
|
end
|
31
27
|
|
32
28
|
it "should halt the reactor" do
|
33
|
-
em
|
34
|
-
em.start
|
29
|
+
@em.start
|
35
30
|
|
36
|
-
em.halt
|
37
|
-
em.join
|
38
|
-
em.reactor_running?.should be_false
|
39
|
-
em.reactor_thread.should be_nil
|
31
|
+
@em.halt
|
32
|
+
@em.join
|
33
|
+
@em.reactor_running?.should be_false
|
34
|
+
@em.reactor_thread.should be_nil
|
40
35
|
end
|
41
36
|
|
42
37
|
it "should dispatch all requests to eventmachine" do
|
43
|
-
em
|
44
|
-
em.start
|
38
|
+
@em.start
|
45
39
|
|
46
40
|
invoked = false
|
47
41
|
m,c = Mutex.new, ConditionVariable.new
|
48
|
-
em.schedule {
|
42
|
+
@em.schedule {
|
49
43
|
invoked = true
|
50
44
|
m.synchronize { c.signal }
|
51
45
|
}
|
data/specs/node_spec.rb
CHANGED
@@ -21,27 +21,26 @@ module RJR
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should start the thread pool" do
|
24
|
-
ThreadPool.instance.stop.join
|
25
24
|
node = Node.new
|
26
|
-
|
25
|
+
node.class.class_variable_get(:@@tp).should be_running
|
27
26
|
end
|
28
27
|
|
29
28
|
it "should start event machine" do
|
30
|
-
|
29
|
+
EventMachine.stop_event_loop
|
31
30
|
node = Node.new
|
32
|
-
|
31
|
+
EventMachine.reactor_running?.should be_true
|
33
32
|
end
|
34
33
|
|
35
34
|
it "should halt the thread pool" do
|
36
35
|
node = Node.new
|
37
36
|
node.halt.join
|
38
|
-
|
37
|
+
node.class.class_variable_get(:@@tp).should_not be_running
|
39
38
|
end
|
40
39
|
|
41
40
|
it "should halt event machine" do
|
42
41
|
node = Node.new
|
43
42
|
node.halt.join
|
44
|
-
|
43
|
+
EventMachine.reactor_running?.should be_false
|
45
44
|
end
|
46
45
|
|
47
46
|
it "should handle connection events" do
|
data/specs/nodes/local_spec.rb
CHANGED
@@ -32,16 +32,22 @@ module RJR::Nodes
|
|
32
32
|
|
33
33
|
describe "#notify" do
|
34
34
|
it "should dispatch local notification" do
|
35
|
+
# notify will most likely return before
|
36
|
+
# handler is executed (in seperate thread), wait
|
37
|
+
m,c = Mutex.new, ConditionVariable.new
|
38
|
+
|
35
39
|
invoked = nil
|
36
40
|
node = Local.new :node_id => 'aaa'
|
37
41
|
node.dispatcher.handle('foobar') { |param|
|
38
42
|
invoked = true
|
43
|
+
m.synchronize { c.signal }
|
39
44
|
'retval'
|
40
45
|
}
|
41
46
|
|
42
47
|
res = node.notify 'foobar', 'myparam'
|
43
|
-
invoked.should == true
|
44
48
|
res.should == nil
|
49
|
+
m.synchronize { c.wait m, 0.1 } unless invoked
|
50
|
+
invoked.should == true
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
data/specs/thread_pool_spec.rb
CHANGED
@@ -5,40 +5,35 @@ require 'rjr/thread_pool'
|
|
5
5
|
|
6
6
|
module RJR
|
7
7
|
describe ThreadPool do
|
8
|
-
|
9
|
-
ThreadPool.
|
10
|
-
ThreadPool.instance.join
|
8
|
+
before(:each) do
|
9
|
+
@tp = ThreadPool.new
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
tp
|
15
|
-
ThreadPool.instance.should == tp
|
12
|
+
after(:each) do
|
13
|
+
@tp.stop.join
|
16
14
|
end
|
17
15
|
|
18
16
|
it "should start the thread pool" do
|
19
|
-
tp
|
20
|
-
tp.
|
21
|
-
tp.
|
22
|
-
tp.should be_running
|
17
|
+
@tp.start
|
18
|
+
@tp.instance_variable_get(:@worker_threads).size.should == ThreadPool.num_threads
|
19
|
+
@tp.should be_running
|
23
20
|
end
|
24
21
|
|
25
22
|
it "should stop the thread pool" do
|
26
|
-
tp
|
27
|
-
tp.
|
28
|
-
tp.
|
29
|
-
tp.
|
30
|
-
tp.
|
31
|
-
tp.should_not be_running
|
23
|
+
@tp.start
|
24
|
+
@tp.stop
|
25
|
+
@tp.join
|
26
|
+
@tp.instance_variable_get(:@worker_threads).size.should == 0
|
27
|
+
@tp.should_not be_running
|
32
28
|
end
|
33
29
|
|
34
30
|
it "should run work" do
|
35
|
-
tp
|
36
|
-
tp.start
|
31
|
+
@tp.start
|
37
32
|
|
38
33
|
jobs_executed = []
|
39
34
|
m,c = Mutex.new, ConditionVariable.new
|
40
|
-
tp << ThreadPoolJob.new { jobs_executed << 1 ; m.synchronize { c.signal } }
|
41
|
-
tp << ThreadPoolJob.new { jobs_executed << 2 ; m.synchronize { c.signal } }
|
35
|
+
@tp << ThreadPoolJob.new { jobs_executed << 1 ; m.synchronize { c.signal } }
|
36
|
+
@tp << ThreadPoolJob.new { jobs_executed << 2 ; m.synchronize { c.signal } }
|
42
37
|
|
43
38
|
m.synchronize { c.wait m, 0.1 } unless jobs_executed.include?(1)
|
44
39
|
m.synchronize { c.wait m, 0.1 } unless jobs_executed.include?(2)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rjr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -79,14 +79,15 @@ files:
|
|
79
79
|
- lib/rjr/semaphore.rb
|
80
80
|
- lib/rjr/em_adapter.rb
|
81
81
|
- lib/rjr/errors.rb
|
82
|
+
- lib/rjr/inspect.rb
|
82
83
|
- lib/rjr/node.rb
|
83
84
|
- lib/rjr/common.rb
|
84
85
|
- lib/rjr/thread_pool.rb
|
85
86
|
- lib/rjr/message.rb
|
86
|
-
- lib/rjr/stats.rb
|
87
87
|
- lib/rjr/nodes/web.rb
|
88
88
|
- lib/rjr/nodes/udp.rb
|
89
89
|
- lib/rjr/nodes/tcp.rb
|
90
|
+
- lib/rjr/nodes/ssh.rb
|
90
91
|
- lib/rjr/nodes/ws.rb
|
91
92
|
- lib/rjr/nodes/tcp2.rb
|
92
93
|
- lib/rjr/nodes/local.rb
|
@@ -97,7 +98,6 @@ files:
|
|
97
98
|
- lib/rjr/dispatcher.rb
|
98
99
|
- specs/dispatcher_spec.rb
|
99
100
|
- specs/node_spec.rb
|
100
|
-
- specs/stats_spec.rb
|
101
101
|
- specs/nodes/multi_spec.rb
|
102
102
|
- specs/nodes/tcp_spec.rb
|
103
103
|
- specs/nodes/web_spec.rb
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- specs/nodes/amqp_spec.rb
|
106
106
|
- specs/nodes/ws_spec.rb
|
107
107
|
- specs/nodes/easy_spec.rb
|
108
|
+
- specs/inspect_spec.rb
|
108
109
|
- specs/thread_pool_spec.rb
|
109
110
|
- specs/message_spec.rb
|
110
111
|
- specs/em_adapter_spec.rb
|