rjr 0.12.2 → 0.15.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.
Files changed (65) hide show
  1. data/README.md +49 -36
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +11 -9
  4. data/bin/rjr-server +12 -10
  5. data/examples/amqp.rb +29 -0
  6. data/examples/client.rb +32 -0
  7. data/examples/complete.rb +36 -0
  8. data/examples/local.rb +29 -0
  9. data/examples/server.rb +26 -0
  10. data/examples/tcp.rb +29 -0
  11. data/examples/web.rb +22 -0
  12. data/examples/ws.rb +29 -0
  13. data/lib/rjr/common.rb +7 -12
  14. data/lib/rjr/dispatcher.rb +171 -239
  15. data/lib/rjr/em_adapter.rb +33 -66
  16. data/lib/rjr/message.rb +43 -12
  17. data/lib/rjr/node.rb +197 -103
  18. data/lib/rjr/nodes/amqp.rb +216 -0
  19. data/lib/rjr/nodes/easy.rb +159 -0
  20. data/lib/rjr/nodes/local.rb +118 -0
  21. data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
  22. data/lib/rjr/nodes/multi.rb +79 -0
  23. data/lib/rjr/nodes/tcp.rb +211 -0
  24. data/lib/rjr/nodes/web.rb +197 -0
  25. data/lib/rjr/nodes/ws.rb +187 -0
  26. data/lib/rjr/stats.rb +70 -0
  27. data/lib/rjr/thread_pool.rb +178 -123
  28. data/site/index.html +45 -0
  29. data/site/jquery-latest.js +9404 -0
  30. data/site/jrw.js +297 -0
  31. data/site/json.js +199 -0
  32. data/specs/dispatcher_spec.rb +244 -198
  33. data/specs/em_adapter_spec.rb +52 -80
  34. data/specs/message_spec.rb +223 -197
  35. data/specs/node_spec.rb +67 -163
  36. data/specs/nodes/amqp_spec.rb +82 -0
  37. data/specs/nodes/easy_spec.rb +13 -0
  38. data/specs/nodes/local_spec.rb +72 -0
  39. data/specs/nodes/multi_spec.rb +65 -0
  40. data/specs/nodes/tcp_spec.rb +75 -0
  41. data/specs/nodes/web_spec.rb +77 -0
  42. data/specs/nodes/ws_spec.rb +78 -0
  43. data/specs/stats_spec.rb +59 -0
  44. data/specs/thread_pool_spec.rb +44 -35
  45. metadata +40 -30
  46. data/lib/rjr/amqp_node.rb +0 -330
  47. data/lib/rjr/inspect.rb +0 -65
  48. data/lib/rjr/local_node.rb +0 -150
  49. data/lib/rjr/multi_node.rb +0 -65
  50. data/lib/rjr/tcp_node.rb +0 -323
  51. data/lib/rjr/thread_pool2.rb +0 -272
  52. data/lib/rjr/util.rb +0 -104
  53. data/lib/rjr/web_node.rb +0 -266
  54. data/lib/rjr/ws_node.rb +0 -289
  55. data/lib/rjr.rb +0 -16
  56. data/specs/amqp_node_spec.rb +0 -31
  57. data/specs/inspect_spec.rb +0 -60
  58. data/specs/local_node_spec.rb +0 -43
  59. data/specs/multi_node_spec.rb +0 -45
  60. data/specs/tcp_node_spec.rb +0 -33
  61. data/specs/util_spec.rb +0 -46
  62. data/specs/web_node_spec.rb +0 -32
  63. data/specs/ws_node_spec.rb +0 -32
  64. /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
  65. /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
@@ -1,65 +0,0 @@
1
- # RJR MultiNode Endpoint
2
- #
3
- # Implements the RJR::Node interface to satisty JSON-RPC requests over multiple protocols
4
- #
5
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
6
- # Licensed under the Apache License, Version 2.0
7
-
8
- require 'eventmachine'
9
- require 'rjr/node'
10
- require 'rjr/message'
11
-
12
- module RJR
13
-
14
- # Multiple node definition, allows a developer to easily multiplex transport
15
- # mechanisms to serve JSON-RPC requests over.
16
- #
17
- # @example Listening for json-rpc requests over amqp, tcp, http, and websockets
18
- # # register rjr dispatchers (see RJR::Dispatcher)
19
- # RJR::Dispatcher.add_handler('hello') { |name|
20
- # # optionally use @rjr_node_type to handle different transport types
21
- # "Hello #{name}!"
22
- # }
23
- #
24
- # amqp_server = RJR::TCPNode.new :node_id => 'amqp_server', :broker => 'localhost'
25
- # tcp_server = RJR::TCPNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '7777'
26
- # web_server = RJR::WebNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '80'
27
- # ws_server = RJR::WebNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '8080'
28
- #
29
- # server = RJR::MultiNode.new :node_id => 'server',
30
- # :nodes => [amqp_server, tcp_server, web_server, ws_server]
31
- # server.listen
32
- # server.join
33
- #
34
- # # invoke requests as you normally would via any protocol
35
- #
36
- class MultiNode < RJR::Node
37
- # Return the nodes
38
- attr_reader :nodes
39
-
40
- # MultiNode initializer
41
- # @param [Hash] args the options to create the tcp node with
42
- # @option args [Array<RJR::Node>] :nodes array of nodes to use to listen to new requests on
43
- def initialize(args = {})
44
- super(args)
45
- @nodes = args[:nodes]
46
- end
47
-
48
- # Add node to multinode
49
- # @param [RJR::Node] node the node to add
50
- def <<(node)
51
- @nodes << node
52
- end
53
-
54
-
55
- # Instruct Node to start listening for and dispatching rpc requests
56
- #
57
- # Implementation of {RJR::Node#listen}
58
- def listen
59
- @nodes.each { |node|
60
- node.listen
61
- }
62
- end
63
- end
64
-
65
- end
data/lib/rjr/tcp_node.rb DELETED
@@ -1,323 +0,0 @@
1
- # RJR TCP Endpoint
2
- #
3
- # Implements the RJR::Node interface to satisty JSON-RPC requests over the TCP protocol
4
- #
5
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
6
- # Licensed under the Apache License, Version 2.0
7
-
8
- require 'uri'
9
- require 'socket'
10
- require 'eventmachine'
11
-
12
- require 'rjr/node'
13
- require 'rjr/message'
14
- require 'rjr/message'
15
- require 'rjr/dispatcher'
16
- require 'rjr/errors'
17
- require 'rjr/thread_pool2'
18
-
19
- module RJR
20
-
21
- # TCP node callback interface, used to invoke json-rpc methods
22
- # against a remote node via a tcp socket connection previously opened
23
- #
24
- # After a node sends a json-rpc request to another, the either node may send
25
- # additional requests to each other via the socket already established until
26
- # it is closed on either end
27
- class TCPNodeCallback
28
-
29
- # TCPNodeCallback initializer
30
- # @param [Hash] args the options to create the tcp node callback with
31
- # @option args [TCPNodeEndpoint] :endpoint tcp node endpoint used to send/receive messages
32
- # @option args [Hash] :headers hash of rjr message headers present in client request when callback is established
33
- def initialize(args = {})
34
- @endpoint = args[:endpoint]
35
- @message_headers = args[:headers]
36
- end
37
-
38
- # Implementation of {RJR::NodeCallback#invoke}
39
- def invoke(callback_method, *data)
40
- msg = NotificationMessage.new :method => callback_method, :args => data, :headers => @message_headers
41
- # TODO surround w/ begin/rescue block incase of socket errors / raise RJR::ConnectionError
42
- @endpoint.safe_send msg.to_s
43
- end
44
- end
45
-
46
- # @private
47
- # Helper class intialized by eventmachine encapsulating a socket connection
48
- class TCPNodeEndpoint < EventMachine::Connection
49
-
50
- attr_reader :host
51
- attr_reader :port
52
-
53
- # TCPNodeEndpoint intializer
54
- #
55
- # specify the TCPNode establishing the connection
56
- def initialize(args = {})
57
- @rjr_node = args[:rjr_node]
58
- @host = args[:host]
59
- @port = args[:port]
60
-
61
- # used to serialize requests to send data via a connection
62
- @send_lock = Mutex.new
63
- end
64
-
65
- # {EventMachine::Connection#receive_data} callback, handle request / response messages
66
- def receive_data(data)
67
- # a large json-rpc message may be split over multiple packets (invocations of receive_data)
68
- # and multiple messages may be concatinated into one packet
69
- @data ||= ""
70
- @data += data
71
- while extracted = MessageUtil.retrieve_json(@data)
72
- msg, @data = *extracted
73
- if RequestMessage.is_request_message?(msg)
74
- ThreadPool2Manager << ThreadPool2Job.new(msg) { |m| handle_request(m, false) }
75
-
76
- elsif NotificationMessage.is_notification_message?(msg)
77
- ThreadPool2Manager << ThreadPool2Job.new(msg) { |m| handle_request(m, true) }
78
-
79
- elsif ResponseMessage.is_response_message?(msg)
80
- handle_response(msg)
81
-
82
- end
83
- end
84
- end
85
-
86
- # {EventMachine::Connection#unbind} callback, connection was closed
87
- def unbind
88
- end
89
-
90
- # Helper to send data safely, this should be invoked instead of send_data
91
- # in all cases
92
- def safe_send(data)
93
- @send_lock.synchronize{
94
- send_data(data)
95
- }
96
- end
97
-
98
-
99
- private
100
-
101
- # Internal helper, handle request message received
102
- def handle_request(data, notification=false)
103
- # XXX hack to handle client disconnection (should grap port/ip immediately on connection and use that)
104
- client_port,client_ip = nil,nil
105
- begin
106
- client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
107
- rescue Exception=>e
108
- end
109
-
110
- msg = notification ? NotificationMessage.new(:message => data, :headers => @rjr_node.message_headers) :
111
- RequestMessage.new(:message => data, :headers => @rjr_node.message_headers)
112
- headers = @rjr_node.message_headers.merge(msg.headers)
113
- result = Dispatcher.dispatch_request(msg.jr_method,
114
- :method_args => msg.jr_args,
115
- :headers => headers,
116
- :client_ip => client_ip,
117
- :client_port => client_port,
118
- :rjr_node => @rjr_node,
119
- :rjr_node_id => @rjr_node.node_id,
120
- :rjr_node_type => TCPNode::RJR_NODE_TYPE,
121
- :rjr_callback =>
122
- TCPNodeCallback.new(:endpoint => self,
123
- :headers => headers))
124
- unless notification
125
- response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
126
- safe_send(response.to_s)
127
- end
128
- end
129
-
130
- # Internal helper, handle response message received
131
- def handle_response(data)
132
- msg = ResponseMessage.new(:message => data, :headers => @rjr_node.message_headers)
133
- res = err = nil
134
- begin
135
- res = Dispatcher.handle_response(msg.result)
136
- rescue Exception => e
137
- err = e
138
- end
139
-
140
- @rjr_node.response_lock.synchronize {
141
- result = [msg.msg_id, res]
142
- result << err if !err.nil?
143
- @rjr_node.responses << result
144
- @rjr_node.response_cv.signal
145
- }
146
- end
147
- end
148
-
149
- # TCP node definition, listen for and invoke json-rpc requests via TCP sockets
150
- #
151
- # Clients should specify the hostname / port when listening for requests and
152
- # when invoking them.
153
- #
154
- # @example Listening for json-rpc requests over tcp
155
- # # register rjr dispatchers (see RJR::Dispatcher)
156
- # RJR::Dispatcher.add_handler('hello') { |name|
157
- # "Hello #{name}!"
158
- # }
159
- #
160
- # # initialize node, listen, and block
161
- # server = RJR::TCPNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
162
- # server.listen
163
- # server.join
164
- #
165
- # @example Invoking json-rpc requests over tcp
166
- # client = RJR::TCPNode.new :node_id => 'client', :host => 'localhost', :port => '8888'
167
- # puts client.invoke_request('jsonrpc://localhost:7777', 'hello', 'mo')
168
- #
169
- class TCPNode < RJR::Node
170
- RJR_NODE_TYPE = :tcp
171
-
172
- attr_accessor :connections
173
-
174
- attr_accessor :response_lock
175
- attr_accessor :response_cv
176
- attr_accessor :responses
177
-
178
- private
179
- # Internal helper, initialize new connection
180
- def init_node(args={}, &on_init)
181
- host,port = args[:host], args[:port]
182
- connection = nil
183
- @connections_lock.synchronize {
184
- connection = @connections.find { |c|
185
- port == c.port && host == c.host
186
- }
187
- if connection.nil?
188
- connection =
189
- EventMachine::connect host, port,
190
- TCPNodeEndpoint, args
191
- @connections << connection
192
- end
193
- }
194
- on_init.call(connection)
195
- end
196
-
197
- # Internal helper, block until response matching message id is received
198
- def wait_for_result(message)
199
- res = nil
200
- while res.nil?
201
- @response_lock.synchronize{
202
- # FIXME throw err if more than 1 match found
203
- res = @responses.select { |response| message.msg_id == response.first }.first
204
- if !res.nil?
205
- @responses.delete(res)
206
-
207
- else
208
- @response_cv.signal
209
- @response_cv.wait @response_lock
210
-
211
- end
212
- }
213
- end
214
- return res
215
- end
216
-
217
- public
218
- # TCPNode initializer
219
- # @param [Hash] args the options to create the tcp node with
220
- # @option args [String] :host the hostname/ip which to listen on
221
- # @option args [Integer] :port the port which to listen on
222
- def initialize(args = {})
223
- super(args)
224
- @host = args[:host]
225
- @port = args[:port]
226
-
227
- @connections = []
228
- @connections_lock = Mutex.new
229
-
230
- @response_lock = Mutex.new
231
- @response_cv = ConditionVariable.new
232
- @responses = []
233
-
234
- @connection_event_handlers = {:closed => [], :error => []}
235
- end
236
-
237
- # Register connection event handler
238
- # @param [:error, :close] event the event to register the handler for
239
- # @param [Callable] handler block param to be added to array of handlers that are called when event occurs
240
- # @yield [TCPNode] self is passed to each registered handler when event occurs
241
- def on(event, &handler)
242
- if @connection_event_handlers.keys.include?(event)
243
- @connection_event_handlers[event] << handler
244
- end
245
- end
246
-
247
- # Instruct Node to start listening for and dispatching rpc requests
248
- #
249
- # Implementation of {RJR::Node#listen}
250
- def listen
251
- em_run {
252
- EventMachine::start_server @host, @port, TCPNodeEndpoint, { :rjr_node => self }
253
- }
254
- end
255
-
256
- # Instructs node to send rpc request, and wait for / return response.
257
- #
258
- # Do not invoke directly from em event loop or callback as will block the message
259
- # subscription used to receive responses
260
- #
261
- # @param [String] uri location of node to send request to, should be
262
- # in format of jsonrpc://hostname:port
263
- # @param [String] rpc_method json-rpc method to invoke on destination
264
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
265
- def invoke_request(uri, rpc_method, *args)
266
- uri = URI.parse(uri)
267
- host,port = uri.host, uri.port
268
-
269
- message = RequestMessage.new :method => rpc_method,
270
- :args => args,
271
- :headers => @message_headers
272
- em_run{
273
- init_node(:host => host, :port => port,
274
- :rjr_node => self) { |c|
275
- c.safe_send message.to_s
276
- }
277
- }
278
-
279
- # TODO optional timeout for response ?
280
- result = wait_for_result(message)
281
-
282
- if result.size > 2
283
- raise Exception, result[2]
284
- end
285
- return result[1]
286
- end
287
-
288
- # Instructs node to send rpc notification (immadiately returns / no response is generated)
289
- #
290
- # @param [String] uri location of node to send notification to, should be
291
- # in format of jsonrpc://hostname:port
292
- # @param [String] rpc_method json-rpc method to invoke on destination
293
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
294
- def send_notification(uri, rpc_method, *args)
295
- # will block until message is published
296
- published_l = Mutex.new
297
- published_c = ConditionVariable.new
298
-
299
- uri = URI.parse(uri)
300
- host,port = uri.host, uri.port
301
-
302
- invoked = false
303
- conn = nil
304
- message = NotificationMessage.new :method => rpc_method,
305
- :args => args,
306
- :headers => @message_headers
307
- em_run{
308
- init_node(:host => host, :port => port,
309
- :rjr_node => self) { |c|
310
- conn = c
311
- c.safe_send message.to_s
312
- # XXX big bug w/ tcp node, this should be invoked only when
313
- # we are sure event machine sent message
314
- published_l.synchronize { invoked = true ; published_c.signal }
315
- }
316
- }
317
- published_l.synchronize { published_c.wait published_l unless invoked }
318
- #sleep 0.01 until conn.get_outbound_data_size == 0
319
- nil
320
- end
321
- end
322
-
323
- end # module RJR
@@ -1,272 +0,0 @@
1
- # Thread Pool (second implementation)
2
- #
3
- # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the Apache License, Version 2.0
5
-
6
- require 'singleton'
7
-
8
- # Work item to be executed in a thread launched by {ThreadPool2}.
9
- #
10
- # The end user just need to initialize this class with the handle
11
- # to the job to be executed and the params to pass to it, before
12
- # handing it off to the thread pool that will take care of the rest.
13
- class ThreadPool2Job
14
- attr_accessor :handler
15
- attr_accessor :params
16
-
17
- # used internally by the thread pool system, these shouldn't
18
- # be set or used by the end user
19
- attr_accessor :timestamp
20
- attr_accessor :thread
21
- attr_accessor :pool_lock
22
- attr_reader :being_executed
23
-
24
- # ThreadPoolJob initializer
25
- # @param [Array] params arguments to pass to the job when it is invoked
26
- # @param [Callable] block handle to callable object corresponding to job to invoke
27
- def initialize(*params, &block)
28
- @params = params
29
- @handler = block
30
- @being_executed = false
31
- @timestamp = nil
32
- end
33
-
34
- # Return string representation of thread pool job
35
- def to_s
36
- "thread_pool2_job-#{@handler.source_location}-#{@params}"
37
- end
38
-
39
- def being_executed?
40
- @being_executed
41
- end
42
-
43
- def completed?
44
- !@timestamp.nil? && !@being_executed
45
- end
46
-
47
- # Set job metadata and execute job with specified params
48
- def exec
49
- # synchronized so that both timestamp is set and being_executed
50
- # set to true before the possiblity of a timeout management
51
- # check (see handle_timeout! below)
52
- @pool_lock.synchronize{
53
- @thread = Thread.current
54
- @being_executed = true
55
- @timestamp = Time.now
56
- }
57
-
58
- @handler.call *@params
59
-
60
- # synchronized so as to ensure that a timeout check does not
61
- # occur until before (in which case thread is killed during
62
- # the check as one atomic operation) or after (in which case
63
- # job is marked as completed, and thread is not killed / goes
64
- # onto pull anther job)
65
- @pool_lock.synchronize{
66
- @being_executed = false
67
- }
68
- end
69
-
70
- # Check timeout and kill thread if it exceeded.
71
- def handle_timeout!(timeout)
72
- # Synchronized so that check and kill operation occur as an
73
- # atomic operation, see exec above
74
- @pool_lock.synchronize {
75
- if @being_executed && (Time.now - @timestamp) > timeout
76
- RJR::Logger.debug "timeout detected on thread #{@thread} started at #{@timestamp}"
77
- @thread.kill
78
- return true
79
- end
80
- return false
81
- }
82
- end
83
- end
84
-
85
- # Utility to launches a specified number of threads on instantiation,
86
- # assigning work to them in order as it arrives.
87
- #
88
- # Supports optional timeout which allows the developer to kill and restart
89
- # threads if a job is taking too long to run.
90
- #
91
- # Second (and hopefully better) thread pool implementation.
92
- #
93
- # TODO move to the RJR namespace
94
- class ThreadPool2
95
- private
96
-
97
- # Internal helper, launch worker thread
98
- #
99
- # Should only be launched from within the pool_lock
100
- def launch_worker
101
- @worker_threads << Thread.new {
102
- while work = @work_queue.pop
103
- begin
104
- #RJR::Logger.debug "launch thread pool job #{work}"
105
- work.pool_lock = @pool_lock
106
- @running_queue << work
107
- work.exec
108
- # TODO cleaner / more immediate way to pop item off running_queue
109
- #RJR::Logger.debug "finished thread pool job #{work}"
110
- rescue Exception => e
111
- # FIXME also send to rjr logger at a critical level
112
- puts "Thread raised Fatal Exception #{e}"
113
- puts "\n#{e.backtrace.join("\n")}"
114
- end
115
- end
116
- } unless @worker_threads.size == @num_threads
117
- end
118
-
119
- # Internal helper, performs checks on workers
120
- def check_workers
121
- if @terminate
122
- @pool_lock.synchronize {
123
- @worker_threads.each { |t|
124
- t.kill
125
- }
126
- @worker_threads = []
127
- }
128
-
129
- elsif @timeout
130
- readd = []
131
- while @running_queue.size > 0 && work = @running_queue.pop
132
- if @timeout && work.handle_timeout!(@timeout)
133
- @pool_lock.synchronize {
134
- @worker_threads.delete(work.thread)
135
- launch_worker
136
- }
137
- elsif !work.completed?
138
- readd << work
139
- end
140
- end
141
- readd.each { |work| @running_queue << work }
142
- end
143
- end
144
-
145
- # Internal helper, launch management thread
146
- #
147
- # Should only be launched from within the pool_lock
148
- def launch_manager
149
- @manager_thread = Thread.new {
150
- until @terminate
151
- # sleep needs to occur b4 check workers so
152
- # workers are guaranteed to be terminated on @terminate
153
- # !FIXME! this enforces a mandatory setting of @timeout which was never intended:
154
- sleep @timeout
155
- check_workers
156
- end
157
- check_workers
158
- @pool_lock.synchronize { @manager_thread = nil }
159
- } unless @manager_thread
160
- end
161
-
162
- public
163
- # Create a thread pool with a specified number of threads
164
- # @param [Integer] num_threads the number of worker threads to create
165
- # @param [Hash] args optional arguments to initialize thread pool with
166
- # @option args [Integer] :timeout optional timeout to use to kill long running worker jobs
167
- def initialize(num_threads, args = {})
168
- @work_queue = Queue.new
169
- @running_queue = Queue.new
170
-
171
- @num_threads = num_threads
172
- @pool_lock = Mutex.new
173
- @worker_threads = []
174
-
175
- @timeout = args[:timeout]
176
-
177
- ObjectSpace.define_finalizer(self, self.class.finalize(self))
178
- end
179
-
180
- # Return internal thread pool state in string
181
- def inspect
182
- "wq#{@work_queue.size}/\
183
- rq#{@running_queue.size}/\
184
- nt#{@num_threads.size}/\
185
- wt#{@worker_threads.select { |wt| ['sleep', 'run'].include?(wt.status) }.size}ok-\
186
- #{@worker_threads.select { |wt| ['aborting', false, nil].include?(wt.status) }.size}nok/\
187
- to#{@timeout}"
188
- end
189
-
190
- # Start the thread pool
191
- def start
192
- # clear work and timeout queues?
193
- @pool_lock.synchronize {
194
- @terminate = false
195
- launch_manager
196
- 0.upto(@num_threads) { |i| launch_worker }
197
- }
198
- end
199
-
200
- # Ruby ObjectSpace finalizer to ensure that thread pool terminates all
201
- # threads when object is destroyed
202
- def self.finalize(thread_pool)
203
- proc { thread_pool.stop ; thread_pool.join }
204
- end
205
-
206
- # Return boolean indicating if thread pool is running.
207
- #
208
- # If at least one worker thread isn't terminated, the pool is still considered running
209
- def running?
210
- @pool_lock.synchronize { @worker_threads.size != 0 && @worker_threads.all? { |t| t.status } }
211
- end
212
-
213
- # Add work to the pool
214
- # @param [ThreadPool2Job] work job to execute in first available thread
215
- def <<(work)
216
- # TODO option to increase worker threads if work queue gets saturated
217
- @work_queue.push work
218
- end
219
-
220
- # Terminate the thread pool, stopping all worker threads
221
- def stop
222
- @pool_lock.synchronize {
223
- @terminate = true
224
-
225
- # wakeup management thread so it can kill workers
226
- # before terminating on its own
227
- begin
228
- @manager_thread.wakeup
229
-
230
- # incase thread wakes up / terminates on its own
231
- rescue ThreadError
232
-
233
- end
234
- }
235
- join
236
- end
237
-
238
- # Block until all worker threads have finished executing
239
- def join
240
- #@pool_lock.synchronize { @worker_threads.each { |t| t.join unless @terminate } }
241
- th = nil
242
- @pool_lock.synchronize { th = @manager_thread if @manager_thread }
243
- th.join if th
244
- end
245
- end
246
-
247
- # Providers an interface to access a shared thread pool.
248
- #
249
- # Thread pool operations may be invoked on this class after
250
- # the 'init' method is called
251
- #
252
- # ThreadPool2Manager.init
253
- # ThreadPool2Manager << ThreadPool2Job(:foo) { "do something" }
254
- class ThreadPool2Manager
255
- # Initialize thread pool if it doesn't exist
256
- def self.init(num_threads, params = {})
257
- if @thread_pool.nil?
258
- @thread_pool = ThreadPool2.new(num_threads, params)
259
- end
260
- @thread_pool.start
261
- end
262
-
263
- # Return shared thread pool
264
- def self.thread_pool
265
- @thread_pool
266
- end
267
-
268
- # Delegates all methods invoked on calls to thread pool
269
- def self.method_missing(method_id, *args, &bl)
270
- @thread_pool.send method_id, *args, &bl
271
- end
272
- end