zmqmachine 0.6.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +15 -0
  2. data/AUTHORS.txt +3 -0
  3. data/History.txt +19 -0
  4. data/examples/fake_ftp.rb +9 -1
  5. data/examples/one_handed_ping_pong.rb +2 -1
  6. data/examples/ping_pong.rb +2 -2
  7. data/examples/pub_sub.rb +10 -4
  8. data/examples/pubsub_forwarder.rb +8 -3
  9. data/examples/throttled_ping_pong.rb +2 -2
  10. data/lib/zm/configuration.rb +129 -0
  11. data/lib/zm/device.rb +4 -0
  12. data/lib/zm/device/configuration.rb +24 -0
  13. data/lib/zm/{devices → device}/forwarder.rb +48 -46
  14. data/lib/zm/{devices → device}/queue.rb +63 -44
  15. data/lib/zm/log_client.rb +2 -2
  16. data/lib/zm/log_server.rb +68 -0
  17. data/lib/zm/reactor.rb +42 -29
  18. data/lib/zm/server.rb +4 -0
  19. data/lib/zm/server/base.rb +139 -0
  20. data/lib/zm/server/configuration.rb +53 -0
  21. data/lib/zm/server/pair.rb +23 -0
  22. data/lib/zm/server/pub.rb +23 -0
  23. data/lib/zm/server/pull.rb +24 -0
  24. data/lib/zm/server/push.rb +23 -0
  25. data/lib/zm/server/rep.rb +42 -0
  26. data/lib/zm/server/req.rb +41 -0
  27. data/lib/zm/server/routing_envelope.rb +26 -0
  28. data/lib/zm/server/sub.rb +40 -0
  29. data/lib/zm/sockets.rb +1 -1
  30. data/lib/zm/sockets/base.rb +3 -12
  31. data/lib/zm/sockets/envelope_help.rb +49 -0
  32. data/lib/zm/sockets/pair.rb +0 -1
  33. data/lib/zm/sockets/pub.rb +0 -1
  34. data/lib/zm/sockets/rep.rb +45 -0
  35. data/lib/zm/sockets/req.rb +46 -1
  36. data/lib/zm/sockets/sub.rb +0 -1
  37. data/lib/zm/timers.rb +26 -13
  38. data/lib/zmqmachine.rb +1 -1
  39. data/version.txt +1 -1
  40. data/zmqmachine.gemspec +6 -6
  41. metadata +90 -91
  42. data/lib/zm/devices.rb +0 -4
  43. data/lib/zm/sockets/xrep.rb +0 -86
  44. data/lib/zm/sockets/xreq.rb +0 -86
@@ -35,7 +35,6 @@
35
35
  #
36
36
 
37
37
  module ZMQMachine
38
-
39
38
  module Device
40
39
 
41
40
 
@@ -53,102 +52,122 @@ module ZMQMachine
53
52
  #
54
53
  # # the queue creates sockets and binds to both given addresses; all messages get
55
54
  # # routed between the two
56
- # queue = ZM::Device::Queue.new reactor, "tcp://192.168.0.100:5050", "tcp://192.168.0.100:5051"
55
+ # config = ZM::Device::Configuration.new
56
+ # config.reactor = reactor
57
+ # config.incoming_endpoint = "tcp://192.168.0.100:5050"
58
+ # config.outgoing_endpoint = "tcp://192.168.0.100:5051"
59
+ # config.verbose = false
60
+ # config.linger = 10 # ms
61
+ # config.hwm = 0
62
+ # queue = ZM::Device::Queue.new(config)
57
63
  #
58
64
  # # the +client_handler+ internally calls "connect" to the incoming address given above
59
- # client = reactor.req_socket client_handler
60
- # client2 = reactor.req_socket client_handler
65
+ # client = reactor.req_socket(client_handler)
66
+ # client2 = reactor.req_socket(client_handler)
61
67
  #
62
68
  # # the +server_handler+ internally calls "connect" to the outgoing address given above
63
- # server = reactor.rep_socket server_handler
69
+ # server = reactor.rep_socket(server_handler)
64
70
  #
65
71
  class Queue
66
72
 
67
- class Handler
73
+ class XReqHandler
68
74
  attr_accessor :socket_out
69
75
 
70
- def initialize reactor, address, dir, opts = {}
71
- @reactor = reactor
76
+ def initialize(config, address, direction)
77
+ @reactor = config.reactor
72
78
  @address = address
73
- @verbose = opts[:verbose] || false
74
- @opts = opts
75
- @dir = dir
79
+ @verbose = config.verbose
80
+ @config = config
81
+ @direction = direction
76
82
  end
77
83
 
78
- def on_attach socket
79
- set_options socket
80
- rc = socket.bind @address
84
+ def on_attach(socket)
85
+ set_options(socket)
86
+ rc = socket.bind(@address)
81
87
  error_check(rc)
82
- #FIXME: error handling!
83
88
  end
84
89
 
85
- def on_writable socket
86
- @reactor.deregister_writable socket
90
+ def on_writable(socket)
91
+ @reactor.deregister_writable(socket)
87
92
  end
88
93
 
89
- def on_readable socket, messages
90
- messages.each { |msg| @reactor.log(:device, "[Q#{@dir}] [#{msg.copy_out_string}]") } if @verbose
94
+ def on_readable(socket, messages, envelope)
95
+ all = (envelope + messages)
96
+ all.each { |msg| @reactor.log(:device, "[Q#{@direction}] [#{msg.copy_out_string}]") } if @verbose
91
97
 
92
98
  if @socket_out
93
99
  # FIXME: need to be able to handle EAGAIN/failed send
94
- rc = socket_out.send_messages messages
95
- messages.each { |message| message.close }
100
+ rc = socket_out.send_messages(all)
101
+ all.each { |message| message.close }
96
102
  end
97
103
  end
98
104
 
99
- if LibZMQ.version2?
105
+ if ZMQ::LibZMQ.version2?
100
106
 
101
- def set_options socket
102
- error_check(socket.raw_socket.setsockopt(ZMQ::HWM, (@opts[:hwm] || 1)))
103
- error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
107
+ def set_options(socket)
108
+ error_check(socket.raw_socket.setsockopt(ZMQ::HWM, @config.hwm))
109
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, @config.linger))
104
110
  end
105
111
 
106
- elsif LibZMQ.version3?
112
+ elsif ZMQ::LibZMQ.version3?
107
113
 
108
- def set_options socket
109
- error_check(socket.raw_socket.setsockopt(ZMQ::SNDHWM, (@opts[:hwm] || 1)))
110
- error_check(socket.raw_socket.setsockopt(ZMQ::RCVHWM, (@opts[:hwm] || 1)))
111
- error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
114
+ def set_options(socket)
115
+ error_check(socket.raw_socket.setsockopt(ZMQ::SNDHWM, @config.hwm))
116
+ error_check(socket.raw_socket.setsockopt(ZMQ::RCVHWM, @config.hwm))
117
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, @config.linger))
112
118
  end
113
119
 
114
120
  end
115
121
 
116
- def error_check rc
122
+ def error_check(rc)
117
123
  if ZMQ::Util.resultcode_ok?(rc)
118
124
  false
119
125
  else
120
- STDERR.puts "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]"
121
- caller(1).each { |callstack| STDERR.puts(callstack) }
126
+ @reactor.log(:error, "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]")
127
+ caller(1).each { |callstack| @reactor.log(:callstack, callstack) }
122
128
  true
123
129
  end
124
130
  end
125
131
 
126
- end # class Handler
132
+ end # class XReqHandler
133
+
134
+ class XRepHandler < XReqHandler
135
+
136
+ def on_readable(socket, messages, envelope)
137
+ all = envelope + messages
138
+ all.each { |msg| @reactor.log(:device, "[Q#{@direction}] [#{msg.copy_out_string}]") } if @verbose
127
139
 
140
+ if @socket_out
141
+ # FIXME: need to be able to handle EAGAIN/failed send
142
+ rc = socket_out.send_messages(all)
143
+ all.each { |message| message.close }
144
+ end
145
+ end
146
+ end
128
147
 
129
148
  # Takes either a properly formatted string that can be converted into a ZM::Address
130
149
  # or takes a ZM::Address directly.
131
150
  #
132
151
  # Routes all messages received by either address to the other address.
133
152
  #
134
- def initialize reactor, incoming, outgoing, opts = {}
135
- incoming = Address.from_string incoming if incoming.kind_of? String
136
- outgoing = Address.from_string outgoing if outgoing.kind_of? String
153
+ def initialize(config)
154
+ @reactor = config.reactor
155
+ incoming = Address.from_string(config.incoming_endpoint.to_s)
156
+ outgoing = Address.from_string(config.outgoing_endpoint.to_s)
137
157
 
138
158
  # setup the handlers for processing messages
139
- @handler_in = Handler.new reactor, incoming, :in, opts
140
- @handler_out = Handler.new reactor, outgoing, :out, opts
159
+ @handler_in = XRepHandler.new(config, incoming, :in)
160
+ @handler_out = XReqHandler.new(config, outgoing, :out)
141
161
 
142
162
  # create each socket and pass in the appropriate handler
143
- @incoming = reactor.xrep_socket @handler_in
144
- @outgoing = reactor.xreq_socket @handler_out
163
+ @incoming_sock = @reactor.xrep_socket(@handler_in)
164
+ @outgoing_sock = @reactor.xreq_socket(@handler_out)
145
165
 
146
166
  # set each handler's outgoing socket
147
- @handler_in.socket_out = @outgoing
148
- @handler_out.socket_out = @incoming
167
+ @handler_in.socket_out = @outgoing_sock
168
+ @handler_out.socket_out = @incoming_sock
149
169
  end
150
170
  end # class Queue
151
171
 
152
172
  end # module Device
153
-
154
173
  end # module ZMQMachine
@@ -109,14 +109,14 @@ module ZMQMachine
109
109
  @reactor.deregister_writable socket
110
110
  end
111
111
 
112
- if LibZMQ.version2?
112
+ if ZMQ::LibZMQ.version2?
113
113
 
114
114
  def set_options socket
115
115
  socket.raw_socket.setsockopt ZMQ::HWM, 0
116
116
  socket.raw_socket.setsockopt ZMQ::LINGER, 0
117
117
  end
118
118
 
119
- elsif LibZMQ.version3?
119
+ elsif ZMQ::LibZMQ.version3?
120
120
 
121
121
  def set_options socket
122
122
  socket.raw_socket.setsockopt ZMQ::SNDHWM, 0
@@ -0,0 +1,68 @@
1
+ #--
2
+ #
3
+ # Author:: Chuck Remes
4
+ # Homepage:: http://github.com/chuckremes/zmqmachine
5
+ # Date:: 20111101
6
+ #
7
+ #----------------------------------------------------------------------------
8
+ #
9
+ # Copyright (C) 2011 by Chuck Remes. All Rights Reserved.
10
+ # Email: cremes at mac dot com
11
+ #
12
+ # (The MIT License)
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining
15
+ # a copy of this software and associated documentation files (the
16
+ # 'Software'), to deal in the Software without restriction, including
17
+ # without limitation the rights to use, copy, modify, merge, publish,
18
+ # distribute, sublicense, and/or sell copies of the Software, and to
19
+ # permit persons to whom the Software is furnished to do so, subject to
20
+ # the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be
23
+ # included in all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
26
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
29
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
30
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
31
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+ module ZMQMachine
38
+
39
+ class LogServer
40
+ include Server::SUB
41
+
42
+ def initialize(configuration)
43
+ configuration.on_read = method(:on_read)
44
+ super
45
+
46
+ @file = configuration.extra[:file] || STDOUT
47
+ end
48
+
49
+ # Writes all messages to the file.
50
+ #
51
+ def on_read socket, messages
52
+ string = ''
53
+ messages.each_with_index do |message, index|
54
+ string << '|' if index > 0
55
+ string << "#{message.copy_out_string}"
56
+ end
57
+
58
+ @file.print "#{string}\n"
59
+ @file.flush
60
+ end
61
+
62
+ def write messages
63
+ # no op
64
+ end
65
+
66
+ end # class LogServer
67
+
68
+ end
@@ -39,46 +39,58 @@ module ZMQMachine
39
39
  class Reactor
40
40
  attr_reader :name, :context, :logger, :exception_handler
41
41
 
42
+ # Takes a ZMQ::Configuration instance to initialize itself.
43
+ #
42
44
  # +name+ provides a name for this reactor instance. It's unused
43
45
  # at present but may be used in the future for allowing multiple
44
- # reactors to communicate amongst each other.
46
+ # reactors to communicate amongst each other. Defaults to 'unnamed'
47
+ # if it isn't set.
45
48
  #
46
49
  # +poll_interval+ is the number of milliseconds to block while
47
- # waiting for new 0mq socket events; default is 10
50
+ # waiting for new 0mq socket events; default is 10 ms.
48
51
  #
49
- # +opts+ may contain a key +:zeromq_context+. When this
50
- # hash is provided, the value for :zeromq_context should be a
52
+ # +context+ should be a
51
53
  # 0mq context as created by ZMQ::Context.new. The purpose of
52
54
  # providing a context to the reactor is so that multiple
53
55
  # reactors can share a single context. Doing so allows for sockets
54
56
  # within each reactor to communicate with each other via an
55
57
  # :inproc transport (:inproc is misnamed, it should be :incontext).
56
58
  # By not supplying this hash, the reactor will create and use
57
- # its own 0mq context.
59
+ # its own 0mq context. Default is nil.
58
60
  #
59
- # +opts+ may also include a +:log_transport+ key. This should be
61
+ # +log_endpoint+ is a
60
62
  # a transport string for an endpoint that a logger client may connect
61
63
  # to for publishing log messages. when this key is defined, the
62
64
  # client is automatically created and connected to the indicated
63
- # endpoint.
64
- #
65
- # Lastly, +opts+ may include a +exception_handler+ key. The exception
66
- # handler should respond to #call and take a single argument.
67
- #
68
- def initialize name, poll_interval = 10, opts = {}
69
- @name = name
65
+ # endpoint. Default is nil.
66
+ #
67
+ # +exception_handler+ is called for all exceptions. The
68
+ # handler should respond to #call and take a single argument. Default
69
+ # is to just raise the exception and exit.
70
+ #
71
+ # config = ZM::Configuration.new
72
+ # config.context = master_context
73
+ # config.log_endpoint = endpoint
74
+ # config.name = :test_rig
75
+ # config.poll_interval = 100 # defaults to 10 if unset
76
+ # Reactor.new(config).run do |reactor|
77
+ # reactor.oneshot_timer(50) { print("At least 50ms have elapsed\n")}
78
+ # end
79
+ #
80
+ def initialize configuration = nil
81
+ configuration ||= Configuration.new
82
+ @name = configuration.name || 'unnamed'
70
83
  @running = false
71
84
  @thread = nil
72
- @poll_interval = determine_interval poll_interval
73
- @timers = ZMQMachine::Timers.new
85
+ @poll_interval = determine_interval(configuration.poll_interval || 10)
74
86
 
75
87
  @proc_queue = []
76
88
  @proc_queue_mutex = Mutex.new
77
89
 
78
90
  # could raise if it fails to allocate a Context
79
- @context = if opts[:zeromq_context]
91
+ @context = if configuration.context
80
92
  @shared_context = true
81
- opts[:zeromq_context]
93
+ configuration.context
82
94
  else
83
95
  @shared_context = false
84
96
  ZMQ::Context.new
@@ -89,14 +101,13 @@ module ZMQMachine
89
101
  @raw_to_socket = {}
90
102
  Thread.abort_on_exception = true
91
103
 
92
- if opts[:log_transport]
93
- @logger = LogClient.new self, opts[:log_transport]
104
+ if configuration.log_endpoint
105
+ @logger = LogClient.new self, configuration.log_endpoint
94
106
  @logging_enabled = true
95
107
  end
96
108
 
97
- if opts[:exception_handler]
98
- @exception_handler = opts[:exception_handler]
99
- end
109
+ @exception_handler = configuration.exception_handler if configuration.exception_handler
110
+ @timers = ZMQMachine::Timers.new(@exception_handler)
100
111
  end
101
112
 
102
113
  def shared_context?
@@ -249,6 +260,7 @@ module ZMQMachine
249
260
  def xreq_socket handler_instance
250
261
  create_socket handler_instance, ZMQMachine::Socket::XReq
251
262
  end
263
+ alias :dealer_socket :xreq_socket
252
264
 
253
265
  # Creates a XREP socket and attaches +handler_instance+ to the
254
266
  # resulting socket. Should only be paired with one other
@@ -264,6 +276,7 @@ module ZMQMachine
264
276
  def xrep_socket handler_instance
265
277
  create_socket handler_instance, ZMQMachine::Socket::XRep
266
278
  end
279
+ alias :router_socket :xrep_socket
267
280
 
268
281
  # Creates a PAIR socket and attaches +handler_instance+ to the
269
282
  # resulting socket. Works only with other #pair_socket instances
@@ -437,16 +450,16 @@ module ZMQMachine
437
450
  end
438
451
 
439
452
  # Publishes log messages to an existing transport passed in to the Reactor
440
- # constructor using the :log_transport key.
453
+ # constructor using the :log_endpoint key.
441
454
  #
442
- # Reactor.new :log_transport => 'inproc://reactor_log'
455
+ # Reactor.new :log_endpoint => 'inproc://reactor_log'
443
456
  #
444
457
  # +level+ parameter refers to a key to indicate severity level, e.g. :warn,
445
458
  # :debug, level0, level9, etc.
446
459
  #
447
460
  # +message+ is a plain string that will be written out in its entirety.
448
461
  #
449
- # When no :log_transport was defined when creating the Reactor, all calls
462
+ # When no :log_endpoint was defined when creating the Reactor, all calls
450
463
  # just discard the messages.
451
464
  #
452
465
  # reactor.log(:info, "some message")
@@ -518,11 +531,11 @@ module ZMQMachine
518
531
  sleep(@poll_interval / 1000.0)
519
532
  else
520
533
  rc = @poller.poll @poll_interval
521
- end
522
534
 
523
- if ZMQ::Util.resultcode_ok?(rc)
524
- @poller.readables.each { |sock| @raw_to_socket[sock].resume_read }
525
- @poller.writables.each { |sock| @raw_to_socket[sock].resume_write }
535
+ if ZMQ::Util.resultcode_ok?(rc)
536
+ @poller.readables.each { |sock| @raw_to_socket[sock].resume_read }
537
+ @poller.writables.each { |sock| @raw_to_socket[sock].resume_write }
538
+ end
526
539
  end
527
540
 
528
541
  rc
@@ -0,0 +1,4 @@
1
+
2
+ %w( base routing_envelope req rep pair pub sub push pull configuration ).each do |rb_file|
3
+ require File.join(File.dirname(__FILE__), 'server', rb_file)
4
+ end
@@ -0,0 +1,139 @@
1
+
2
+ module ZMQMachine
3
+
4
+ module Server
5
+
6
+ module Base
7
+ def initialize configuration
8
+ @reactor = configuration.reactor
9
+ @configuration = configuration
10
+
11
+ @on_read = @configuration.on_read
12
+ allocate_socket
13
+
14
+ @message_queue = []
15
+ end
16
+
17
+ def shutdown
18
+ @reactor.log :debug, "#{self.class}#shutdown_socket, closing reactor socket"
19
+ @on_read = nil
20
+ @reactor.close_socket @socket
21
+ end
22
+
23
+ def on_attach socket
24
+ # socket options *must* be set before we bind/connect otherwise they are ignored
25
+ set_options socket
26
+ rc = -1
27
+
28
+ if @configuration.bind
29
+ rc = socket.bind @configuration.endpoint
30
+ @reactor.log :debug, "#{self.class}#on_attach, bind rc [#{rc}], endpoint #{@configuration.endpoint}"
31
+ raise "#{self.class}#on_attach, failed to bind to endpoint [#{@configuration.endpoint}]" unless ZMQ::Util.resultcode_ok?(rc)
32
+ elsif @configuration.connect
33
+ rc = socket.connect @configuration.endpoint
34
+ @reactor.log :debug, "#{self.class}#on_attach, connect rc [#{rc}], endpoint #{@configuration.endpoint}"
35
+ raise "#{self.class}#on_attach, failed to connect to endpoint [#{@configuration.endpoint}]" unless ZMQ::Util.resultcode_ok?(rc)
36
+ end
37
+
38
+
39
+ register_for_events socket
40
+ end
41
+
42
+ # Takes an array of ZM::Message instances and writes them out to the socket. If any
43
+ # socket write fails, the message is saved. We will attempt to write it again in
44
+ # 10 milliseconds or when another message array is sent, whichever comes first.
45
+ #
46
+ # All messages passed here are guaranteed to be written in the *order they were
47
+ # received*.
48
+ #
49
+ def write messages, verbose = false
50
+ @verbose = verbose
51
+ @message_queue << messages
52
+ write_queue_to_socket
53
+ end
54
+
55
+ # Prints each message when global debugging is enabled.
56
+ #
57
+ # Forwards +messages+ on to the :on_read callback given in the constructor.
58
+ #
59
+ def on_readable socket, messages
60
+ @on_read.call socket, messages
61
+ close_messages messages
62
+ end
63
+
64
+ # Just deregisters from receiving any further write *events*
65
+ #
66
+ def on_writable socket
67
+ #@reactor.log :debug, "#{self.class}#on_writable, deregister for writes on sid [#{@session_id}]"
68
+ @reactor.deregister_writable socket
69
+ end
70
+
71
+ def on_readable_error socket, return_code
72
+ STDERR.puts "#{self.class}#on_readable_error, rc [#{return_code}], errno [#{ZMQ::Util.errno}], description [#{ZMQ::Util.error_string}], sock #{socket.inspect}"
73
+ end
74
+
75
+ def on_writable_error socket, return_code
76
+ STDERR.puts "#{self.class}#on_writable_error, rc [#{return_code}], errno [#{ZMQ::Util.errno}], description [#{ZMQ::Util.error_string}], sock #{socket.inspect}"
77
+ end
78
+
79
+
80
+ private
81
+
82
+
83
+ def register_for_events socket
84
+ @reactor.register_readable socket
85
+ @reactor.deregister_writable socket
86
+ end
87
+
88
+ if ZMQ::LibZMQ.version2?
89
+
90
+ def set_options socket
91
+ socket.raw_socket.setsockopt ZMQ::HWM, (@configuration.hwm || 0)
92
+ socket.raw_socket.setsockopt ZMQ::LINGER, (@configuration.linger || 1)
93
+ end
94
+
95
+ elsif ZMQ::LibZMQ.version3?
96
+
97
+ def set_options socket
98
+ socket.raw_socket.setsockopt ZMQ::RCVHWM, (@configuration.hwm || 0)
99
+ socket.raw_socket.setsockopt ZMQ::SNDHWM, (@configuration.hwm || 0)
100
+ socket.raw_socket.setsockopt ZMQ::LINGER, (@configuration.linger || 1)
101
+ end
102
+
103
+ end
104
+
105
+ def write_queue_to_socket
106
+ until @message_queue.empty?
107
+ messages = get_next_message_array
108
+
109
+ rc = @socket.send_messages messages
110
+
111
+ if ZMQ::Util.resultcode_ok?(rc) # succeeded, so remove the message from the queue
112
+ @message_queue.shift
113
+ close_messages messages
114
+ elsif ZMQ::Util.errno == ZMQ::EAGAIN
115
+ # schedule another write attempt in 10 ms; break out of the loop
116
+ @reactor.log :debug, "#{self.class}#write_queue_to_socket, failed to write messages; scheduling next attempt"
117
+ @reactor.oneshot_timer 10, method(:write_queue_to_socket)
118
+ break
119
+ end
120
+ end
121
+ end
122
+
123
+ def get_next_message_array
124
+ messages = @message_queue.at(0)
125
+ messages.each { |message| @reactor.log :write, message.copy_out_string } if @verbose
126
+ messages
127
+ end
128
+
129
+ def close_messages messages
130
+ messages.each { |message| message.close }
131
+ end
132
+
133
+ def allocate_socket() nil; end
134
+
135
+ end # module Base
136
+
137
+ end # module Server
138
+
139
+ end # ZMQMachine