zmqmachine 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ == 0.6.0 / 2011-10-04
2
+ * Compatibility with ffi-rzmq 0.9.0 and added some conditional
3
+ logic to support 0mq 2.1.x and 3.x APIs.
4
+
5
+ * Removed all exceptions and replaced them with return codes.
6
+
7
+ * Added :exception_handler key to Reactor#new so exceptions can
8
+ be managed by user code.
9
+
1
10
  == 0.5.2 / 2011-07-21
2
11
  * Added PUSH and PULL socket types. Never needed them until now.
3
12
  * Added a +context+ reader to the Reactor class.
data/README.rdoc CHANGED
@@ -29,9 +29,9 @@ descriptors. This isn't on my roadmap but patches are accepted.
29
29
 
30
30
  * Some classes are just skeletons.
31
31
 
32
- * Recommended for JRuby since it is the only existing runtime that uses
32
+ * Recommended for JRuby or Rubinius since they are the only existing runtimes that use
33
33
  native threads without a GIL. MRI 1.9.x is okay but may have
34
- threading problems.
34
+ threading problems. This hasn't been tested with MacRuby.
35
35
 
36
36
  == SYNOPSIS:
37
37
 
@@ -41,12 +41,12 @@ Read and execute the examples in the examples directory.
41
41
 
42
42
  Requires the 0mq library
43
43
 
44
- * 0mq 2.0.10 or 2.1 and later
44
+ * 0mq 2.1.x, 3.0 and later
45
45
 
46
46
  Depends on 2 external gems.
47
47
 
48
- * ffi-rzmq (>= 0.7.0)
49
- * ffi (>= 1.0.0)
48
+ * ffi-rzmq (>= 0.9.0)
49
+ * ffi (>= 1.0.0) [MRI only]
50
50
 
51
51
  == INSTALL:
52
52
 
@@ -66,9 +66,13 @@ class SubscriberHandler
66
66
  @received_count += 1
67
67
  sleep 0.01 if @sleep
68
68
  end
69
+
70
+ def on_readable_error socket, rc
71
+ STDERR.puts "got error, rc [#{rc}], errno [#{ZMQ::Util.errno}], descr [#{ZMQ::Util.error_string}]"
72
+ end
69
73
  end
70
74
 
71
- sleep_time = 5
75
+ sleep_time = 10
72
76
 
73
77
 
74
78
  # Run the forwarder device in a separate context. *Could* be run from
data/lib/zm/address.rb CHANGED
@@ -36,8 +36,13 @@
36
36
 
37
37
  module ZMQMachine
38
38
 
39
- #FIXME: needs to handle ipc and inproc transports better, e.g.
40
- # there is no port component to either one.
39
+ # A simple wrapper for creating transport strings for the 0mq
40
+ # library to use for bind and connect calls.
41
+ #
42
+ # It is recommended to use Address.create instead of calling #new
43
+ # directly on the class. Using this factory method allows user code
44
+ # to avoid creating a begin/rescue/end structure and dealing with
45
+ # exceptions. Failed creation will return a nil value.
41
46
  #
42
47
  class Address
43
48
  attr_reader :host, :port, :transport
@@ -57,21 +62,34 @@ module ZMQMachine
57
62
  "#{@transport}://#{@host}"
58
63
  end
59
64
  end
65
+
66
+ # Recommended to use this class method as a factory versus calling
67
+ # #new directly. Returns an Address object upon successful creation
68
+ # and nil if creation fails.
69
+ #
70
+ def self.create host, port, type = :tcp
71
+ address = nil
72
+ begin
73
+ address = Address.new host, port, type
74
+ rescue UnknownAddressError
75
+ address = nil
76
+ end
77
+
78
+ address
79
+ end
60
80
 
61
81
 
62
82
  # Converts strings with the format "type://host:port" into
63
83
  # an Address instance.
64
84
  #
65
85
  def self.from_string string
66
- #FIXME: needs error checking and ability to handle inproc/ipc types
67
- #
68
86
  # should also return nil or some other error indication when parsing fails
69
87
  split = string.split(':')
70
88
  type = split[0]
71
89
  port = split[2] # nil for ipc/inproc and non-empty for tcp
72
90
  host = split[1].slice(2, split[1].length) #sub('//', '')
73
91
 
74
- Address.new host, port, type.downcase.to_sym
92
+ Address.create host, port, type.downcase.to_sym
75
93
  end
76
94
 
77
95
  private
@@ -70,16 +70,16 @@ module ZMQMachine
70
70
  @address = address
71
71
  @verbose = opts[:verbose] || false
72
72
  @opts = opts
73
-
73
+
74
74
  @messages = []
75
75
  end
76
76
 
77
77
  def on_attach socket
78
- socket.identity = "forwarder.#{Kernel.rand(999_999_999)}"
79
78
  set_options socket
80
79
  rc = socket.bind @address
80
+ error_check(rc)
81
81
  #FIXME: error handling!
82
- socket.subscribe_all if :sub == socket.kind
82
+ error_check(socket.subscribe_all) if :sub == socket.kind
83
83
  end
84
84
 
85
85
  def on_writable socket
@@ -91,15 +91,42 @@ module ZMQMachine
91
91
 
92
92
  if @socket_out
93
93
  rc = socket_out.send_messages messages
94
+ error_check(rc)
94
95
  messages.each { |message| message.close }
95
96
  end
96
97
  end
97
-
98
- def set_options socket
99
- socket.raw_socket.setsockopt ZMQ::HWM, (@opts[:hwm] || 1)
100
- socket.raw_socket.setsockopt ZMQ::LINGER, (@opts[:linger] || 0)
98
+
99
+ def on_readable_error socket, return_code
100
+ STDERR.puts "#{self.class}#on_readable_error, rc [#{return_code}], errno [#{ZMQ::Util.errno}], descr [#{ZMQ::Util.error_string}]"
101
+ end
102
+
103
+ if LibZMQ.version2?
104
+
105
+ def set_options socket
106
+ error_check(socket.raw_socket.setsockopt(ZMQ::HWM, (@opts[:hwm] || 1)))
107
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
108
+ end
109
+
110
+ elsif LibZMQ.version3?
111
+
112
+ def set_options socket
113
+ error_check(socket.raw_socket.setsockopt(ZMQ::SNDHWM, (@opts[:hwm] || 1)))
114
+ error_check(socket.raw_socket.setsockopt(ZMQ::RCVHWM, (@opts[:hwm] || 1)))
115
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
116
+ end
117
+
118
+ end
119
+
120
+ def error_check rc
121
+ if ZMQ::Util.resultcode_ok?(rc)
122
+ false
123
+ else
124
+ STDERR.puts "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]"
125
+ caller(1).each { |callstack| STDERR.puts(callstack) }
126
+ true
127
+ end
101
128
  end
102
-
129
+
103
130
  end # class Handler
104
131
 
105
132
 
@@ -76,9 +76,9 @@ module ZMQMachine
76
76
  end
77
77
 
78
78
  def on_attach socket
79
- socket.identity = "queue.#{Kernel.rand(999_999_999)}"
80
79
  set_options socket
81
80
  rc = socket.bind @address
81
+ error_check(rc)
82
82
  #FIXME: error handling!
83
83
  end
84
84
 
@@ -95,12 +95,34 @@ module ZMQMachine
95
95
  messages.each { |message| message.close }
96
96
  end
97
97
  end
98
-
99
- def set_options socket
100
- socket.raw_socket.setsockopt ZMQ::HWM, (@opts[:hwm] || 1)
101
- socket.raw_socket.setsockopt ZMQ::LINGER, (@opts[:linger] || 0)
98
+
99
+ if LibZMQ.version2?
100
+
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)))
104
+ end
105
+
106
+ elsif LibZMQ.version3?
107
+
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)))
112
+ end
113
+
102
114
  end
103
-
115
+
116
+ def error_check rc
117
+ if ZMQ::Util.resultcode_ok?(rc)
118
+ false
119
+ else
120
+ STDERR.puts "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]"
121
+ caller(1).each { |callstack| STDERR.puts(callstack) }
122
+ true
123
+ end
124
+ end
125
+
104
126
  end # class Handler
105
127
 
106
128
 
data/lib/zm/exceptions.rb CHANGED
@@ -36,6 +36,10 @@
36
36
 
37
37
  module ZMQMachine
38
38
 
39
+ class ConnectionError < StandardError; end
40
+
41
+ class SetsockoptError < StandardError; end
42
+
39
43
  class TimeoutError < StandardError; end
40
44
 
41
45
  class UnknownAddressError < StandardError; end
data/lib/zm/log_client.rb CHANGED
@@ -59,7 +59,7 @@ module ZMQMachine
59
59
  #FIXME error check!
60
60
  rc = socket.connect @transport
61
61
 
62
- raise "#{self.class}#on_attach, failed to connect to transport endpoint [#{@transport}]" unless rc
62
+ raise ConnectionError.new("#{self.class}#on_attach, failed to connect to transport endpoint [#{@transport}]") unless ZMQ::Util.resultcode_ok?(rc)
63
63
 
64
64
  register_for_events socket
65
65
  end
@@ -78,7 +78,7 @@ module ZMQMachine
78
78
  @message_queue << [ZMQ::Message.new(level.to_s), ZMQ::Message.new(timestamp), ZMQ::Message.new(message.to_s)]
79
79
  write_queue_to_socket
80
80
  end
81
-
81
+
82
82
  def puts string
83
83
  write 'untagged', string
84
84
  end
@@ -109,26 +109,38 @@ module ZMQMachine
109
109
  @reactor.deregister_writable socket
110
110
  end
111
111
 
112
- def set_options socket
113
- socket.raw_socket.setsockopt ZMQ::HWM, 0
114
- socket.raw_socket.setsockopt ZMQ::LINGER, 0
112
+ if LibZMQ.version2?
113
+
114
+ def set_options socket
115
+ socket.raw_socket.setsockopt ZMQ::HWM, 0
116
+ socket.raw_socket.setsockopt ZMQ::LINGER, 0
117
+ end
118
+
119
+ elsif LibZMQ.version3?
120
+
121
+ def set_options socket
122
+ socket.raw_socket.setsockopt ZMQ::SNDHWM, 0
123
+ socket.raw_socket.setsockopt ZMQ::RCVHWM, 0
124
+ socket.raw_socket.setsockopt ZMQ::LINGER, 0
125
+ end
126
+
115
127
  end
116
128
 
117
129
  def write_queue_to_socket
118
130
  until @message_queue.empty?
119
131
  rc = @socket.send_messages @message_queue.at(0)
120
132
 
121
- if rc # succeeded, so remove the message from the queue
133
+ if ZMQ::Util.resultcode_ok?(rc) # succeeded, so remove the message from the queue
122
134
  messages = @message_queue.shift
123
135
  messages.each { |message| message.close }
124
-
136
+
125
137
  if @timer
126
138
  @timer.cancel
127
139
  @timer = nil
128
140
  end
129
-
141
+
130
142
  else
131
-
143
+
132
144
  # schedule another write attempt in 10 ms; break out of the loop
133
145
  # only set the timer *once*
134
146
  @timer = @reactor.oneshot_timer 10, method(:write_queue_to_socket) unless @timer
data/lib/zm/reactor.rb CHANGED
@@ -37,7 +37,7 @@
37
37
  module ZMQMachine
38
38
 
39
39
  class Reactor
40
- attr_reader :name, :context, :logger
40
+ attr_reader :name, :context, :logger, :exception_handler
41
41
 
42
42
  # +name+ provides a name for this reactor instance. It's unused
43
43
  # at present but may be used in the future for allowing multiple
@@ -62,6 +62,9 @@ module ZMQMachine
62
62
  # client is automatically created and connected to the indicated
63
63
  # endpoint.
64
64
  #
65
+ # Lastly, +opts+ may include a +exception_handler+ key. The exception
66
+ # handler should respond to #call and take a single argument.
67
+ #
65
68
  def initialize name, poll_interval = 10, opts = {}
66
69
  @name = name
67
70
  @running = false
@@ -72,7 +75,7 @@ module ZMQMachine
72
75
  @proc_queue = []
73
76
  @proc_queue_mutex = Mutex.new
74
77
 
75
- # could raise if it fails
78
+ # could raise if it fails to allocate a Context
76
79
  @context = if opts[:zeromq_context]
77
80
  @shared_context = true
78
81
  opts[:zeromq_context]
@@ -90,6 +93,10 @@ module ZMQMachine
90
93
  @logger = LogClient.new self, opts[:log_transport]
91
94
  @logging_enabled = true
92
95
  end
96
+
97
+ if opts[:exception_handler]
98
+ @exception_handler = opts[:exception_handler]
99
+ end
93
100
  end
94
101
 
95
102
  def shared_context?
@@ -211,9 +218,7 @@ module ZMQMachine
211
218
  # All handlers must implement the #on_attach method.
212
219
  #
213
220
  def req_socket handler_instance
214
- sock = ZMQMachine::Socket::Req.new @context, handler_instance
215
- save_socket sock
216
- sock
221
+ create_socket handler_instance, ZMQMachine::Socket::Req
217
222
  end
218
223
 
219
224
  # Creates a REP socket and attaches +handler_instance+ to the
@@ -227,9 +232,7 @@ module ZMQMachine
227
232
  # All handlers must implement the #on_attach method.
228
233
  #
229
234
  def rep_socket handler_instance
230
- sock = ZMQMachine::Socket::Rep.new @context, handler_instance
231
- save_socket sock
232
- sock
235
+ create_socket handler_instance, ZMQMachine::Socket::Rep
233
236
  end
234
237
 
235
238
  # Creates a XREQ socket and attaches +handler_instance+ to the
@@ -244,9 +247,7 @@ module ZMQMachine
244
247
  # All handlers must implement the #on_attach method.
245
248
  #
246
249
  def xreq_socket handler_instance
247
- sock = ZMQMachine::Socket::XReq.new @context, handler_instance
248
- save_socket sock
249
- sock
250
+ create_socket handler_instance, ZMQMachine::Socket::XReq
250
251
  end
251
252
 
252
253
  # Creates a XREP socket and attaches +handler_instance+ to the
@@ -261,9 +262,7 @@ module ZMQMachine
261
262
  # All handlers must implement the #on_attach method.
262
263
  #
263
264
  def xrep_socket handler_instance
264
- sock = ZMQMachine::Socket::XRep.new @context, handler_instance
265
- save_socket sock
266
- sock
265
+ create_socket handler_instance, ZMQMachine::Socket::XRep
267
266
  end
268
267
 
269
268
  # Creates a PAIR socket and attaches +handler_instance+ to the
@@ -279,9 +278,7 @@ module ZMQMachine
279
278
  # All handlers must implement the #on_attach method.
280
279
  #
281
280
  def pair_socket handler_instance
282
- sock = ZMQMachine::Socket::Pair.new @context, handler_instance
283
- save_socket sock
284
- sock
281
+ create_socket handler_instance, ZMQMachine::Socket::Pair
285
282
  end
286
283
 
287
284
  # Creates a PUB socket and attaches +handler_instance+ to the
@@ -296,9 +293,7 @@ module ZMQMachine
296
293
  # All handlers must implement the #on_attach method.
297
294
  #
298
295
  def pub_socket handler_instance
299
- sock = ZMQMachine::Socket::Pub.new @context, handler_instance
300
- save_socket sock
301
- sock
296
+ create_socket handler_instance, ZMQMachine::Socket::Pub
302
297
  end
303
298
 
304
299
  # Creates a SUB socket and attaches +handler_instance+ to the
@@ -313,9 +308,7 @@ module ZMQMachine
313
308
  # All handlers must implement the #on_attach method.
314
309
  #
315
310
  def sub_socket handler_instance
316
- sock = ZMQMachine::Socket::Sub.new @context, handler_instance
317
- save_socket sock
318
- sock
311
+ create_socket handler_instance, ZMQMachine::Socket::Sub
319
312
  end
320
313
 
321
314
  # Creates a PUSH socket and attaches +handler_instance+ to the
@@ -330,9 +323,7 @@ module ZMQMachine
330
323
  # All handlers must implement the #on_attach method.
331
324
  #
332
325
  def push_socket handler_instance
333
- sock = ZMQMachine::Socket::Push.new @context, handler_instance
334
- save_socket sock
335
- sock
326
+ create_socket handler_instance, ZMQMachine::Socket::Push
336
327
  end
337
328
 
338
329
  # Creates a PULL socket and attaches +handler_instance+ to the
@@ -347,9 +338,7 @@ module ZMQMachine
347
338
  # All handlers must implement the #on_attach method.
348
339
  #
349
340
  def pull_socket handler_instance
350
- sock = ZMQMachine::Socket::Pull.new @context, handler_instance
351
- save_socket sock
352
- sock
341
+ create_socket handler_instance, ZMQMachine::Socket::Pull
353
342
  end
354
343
 
355
344
  # Registers the +sock+ for POLLOUT events that will cause the
@@ -475,16 +464,24 @@ module ZMQMachine
475
464
  private
476
465
 
477
466
  def run_once
478
- run_procs
479
- run_timers
480
- poll
467
+ begin
468
+ run_procs
469
+ run_timers
470
+ poll
471
+ rescue => e
472
+ if @exception_handler
473
+ @exception_handler.call(e)
474
+ else
475
+ raise
476
+ end
477
+ end
481
478
  end
482
479
 
483
480
  # Close each open socket and terminate the reactor context; this will
484
481
  # release the native memory backing each of these objects
485
482
  def cleanup
486
483
  @proc_queue_mutex.synchronize { @proc_queue.clear }
487
-
484
+
488
485
  # work on a dup since #close_socket deletes from @sockets
489
486
  @sockets.dup.each { |sock| close_socket sock }
490
487
  @context.terminate unless shared_context?
@@ -510,6 +507,8 @@ module ZMQMachine
510
507
  end
511
508
 
512
509
  def poll
510
+ rc = 0
511
+
513
512
  if (@proc_queue.empty? && @sockets.empty?) || @poller.size.zero?
514
513
  # when there are no sockets registered, @poller.poll would return immediately;
515
514
  # the same is true when sockets are registered but *not* for any events;
@@ -518,11 +517,28 @@ module ZMQMachine
518
517
  # to run (e.g. via next_tick)
519
518
  sleep(@poll_interval / 1000.0)
520
519
  else
521
- @poller.poll @poll_interval
520
+ rc = @poller.poll @poll_interval
521
+ end
522
+
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 }
522
526
  end
523
527
 
524
- @poller.readables.each { |sock| @raw_to_socket[sock].resume_read }
525
- @poller.writables.each { |sock| @raw_to_socket[sock].resume_write }
528
+ rc
529
+ end
530
+
531
+ def create_socket handler_instance, kind
532
+ sock = nil
533
+
534
+ begin
535
+ sock = kind.new @context, handler_instance
536
+ save_socket sock
537
+ rescue ZMQ::ContextError => e
538
+ sock = nil
539
+ end
540
+
541
+ sock
526
542
  end
527
543
 
528
544
  def save_socket sock
@@ -546,7 +562,7 @@ module ZMQMachine
546
562
  # library does this for us.
547
563
  #
548
564
  def determine_interval interval
549
- # set a lower bound of 1000 usec so we don't burn up the CPU
565
+ # set a lower bound of 1 millisec so we don't burn up the CPU
550
566
  interval <= 0 ? 1.0 : interval.to_i
551
567
  end
552
568
 
@@ -42,6 +42,17 @@ module ZMQMachine
42
42
  attr_reader :raw_socket, :kind
43
43
  attr_reader :poll_options
44
44
 
45
+ def self.create context, handler
46
+ socket = nil
47
+ begin
48
+ socket = new context, handler
49
+ rescue => e
50
+ socket = nil
51
+ end
52
+
53
+ socket
54
+ end
55
+
45
56
  def initialize context, handler
46
57
  @context = context
47
58
  @bindings = []
@@ -49,10 +60,11 @@ module ZMQMachine
49
60
 
50
61
  @handler = handler
51
62
  @raw_socket = allocate_socket @context
52
-
63
+
53
64
  # default ZMQ::LINGER to 1 millisecond so closing a socket
54
65
  # doesn't block forever
55
- @raw_socket.setsockopt ZMQ::LINGER, 1
66
+ rc = @raw_socket.setsockopt ZMQ::LINGER, 1
67
+ raise SetsockoptError.new("Setting LINGER on socket failed at line #{caller(0)}") unless ZMQ::Util.resultcode_ok?(rc)
56
68
  attach @handler
57
69
  end
58
70
 
@@ -74,118 +86,82 @@ module ZMQMachine
74
86
  # endpoint.
75
87
  #
76
88
  def bind address
77
- begin
78
- @bindings << address
79
- @raw_socket.bind address.to_s
80
- true
81
- rescue ZMQ::ZeroMQError
82
- @bindings.pop
83
- false
84
- end
89
+ @bindings << address
90
+ rc = @raw_socket.bind address.to_s
91
+ @bindings.pop unless ZMQ::Util.resultcode_ok?(rc)
92
+ rc
85
93
  end
86
94
 
87
95
  # Connect this 0mq socket to the 0mq socket bound to the endpoint
88
96
  # described by the +address+.
89
97
  #
90
98
  def connect address
91
- begin
92
- @connections << address
93
- @raw_socket.connect address.to_s
94
- true
95
- rescue ZMQ::ZeroMQError
96
- @connections.pop
97
- false
98
- end
99
+ @connections << address
100
+ rc = @raw_socket.connect address.to_s
101
+ @connections.pop unless ZMQ::Util.resultcode_ok?(rc)
102
+ rc
99
103
  end
100
104
 
101
105
  # Called to send a ZMQ::Message that was populated with data.
102
106
  #
103
- # Returns true on success, false otherwise.
104
- #
105
- # May raise a ZMQ::SocketError.
107
+ # Returns 0+ on success, -1 for failure. Use ZMQ::Util.resultcode_ok? and
108
+ # ZMQ::Util.errno to check for errors.
106
109
  #
107
110
  def send_message message, multipart = false
108
- begin
109
- queued = @raw_socket.send message, ZMQ::NOBLOCK | (multipart ? ZMQ::SNDMORE : 0)
110
- rescue ZMQ::ZeroMQError => e
111
- queued = false
112
- end
113
- queued
111
+ flag = multipart ? (ZMQ::SNDMORE | ZMQ::Util.nonblocking_flag) : ZMQ::Util.nonblocking_flag
112
+ @raw_socket.send(message, flag)
114
113
  end
115
114
 
116
115
  # Convenience method to send a string on the socket. It handles
117
116
  # the creation of a ZMQ::Message and populates it appropriately.
118
117
  #
119
- # Returns true on success, false otherwise.
120
- #
121
- # May raise a ZMQ::SocketError.
118
+ # Returns 0+ for success, -1 for failure. Check ZMQ::Util.errno for
119
+ # details on the error.
122
120
  #
123
121
  def send_message_string message, multipart = false
124
- queued = @raw_socket.send_string message, ZMQ::NOBLOCK | (multipart ? ZMQ::SNDMORE : 0)
125
- queued
122
+ @raw_socket.send_string message, ZMQ::Util.nonblocking_flag | (multipart ? ZMQ::SNDMORE : 0)
126
123
  end
127
124
 
128
125
  # Convenience method for sending a multi-part message. The
129
- # +messages+ argument must respond to :size, :at and :last (like
130
- # an Array).
131
- #
132
- # May raise a ZMQ::SocketError.
126
+ # +messages+ argument must implement Enumerable.
133
127
  #
134
128
  def send_messages messages
135
- rc = true
136
- i = 0
137
- size = messages.size
138
-
139
- # loop through all messages but the last
140
- while rc && size > 1 && i < size - 1 do
141
- rc = send_message messages.at(i), true
142
- i += 1
143
- end
144
-
145
- # FIXME: bug; if any of the message parts fail (rc != 0) we don't see that here; the
146
- # #send_message function should capture exceptions and turn them into integers for bubbling
147
-
148
- # send the last message without the multipart arg to flush
149
- # the message to the 0mq queue
150
- rc = send_message messages.last if rc && size > 0
151
- rc
129
+ @raw_socket.sendmsgs messages
152
130
  end
153
131
 
154
- # Retrieve the IDENTITY value assigned to this socket.
155
- #
156
- def identity() @raw_socket.identity; end
132
+ if LibZMQ.version2? || LibZMQ.version3?
157
133
 
158
- # Assign a custom IDENTITY value to this socket. Limit is
159
- # 255 bytes and must be greater than 0 bytes.
160
- #
161
- def identity=(value) @raw_socket.identity = value; end
134
+ # Retrieve the IDENTITY value assigned to this socket.
135
+ #
136
+ def identity() @raw_socket.identity; end
137
+
138
+ # Assign a custom IDENTITY value to this socket. Limit is
139
+ # 255 bytes and must be greater than 0 bytes.
140
+ #
141
+ def identity=(value) @raw_socket.identity = value; end
142
+
143
+ end # version check
162
144
 
163
145
  # Used by the reactor. Never called by user code.
164
146
  #
165
- # FIXME: need to rework all of this +rc+ stuff. The underlying lib returns
166
- # nil when a NOBLOCK socket gets EAGAIN. It returns true when a message
167
- # was successfully dequeued. The use of rc here is really ugly and wrong.
168
- #
169
147
  def resume_read
170
148
  rc = 0
171
-
172
- # loop and deliver all messages until the socket returns EAGAIN
173
- while 0 == rc
149
+ more = true
150
+
151
+ while ZMQ::Util.resultcode_ok?(rc) && more
174
152
  parts = []
175
- rc = read_message_part parts
176
- #puts "resume_read: rc1 [#{rc}], more_parts? [#{@raw_socket.more_parts?}]"
153
+ rc = @raw_socket.recvmsgs parts, ZMQ::Util.nonblocking_flag
177
154
 
178
- while 0 == rc && @raw_socket.more_parts?
179
- #puts "get next part"
180
- rc = read_message_part parts
181
- #puts "resume_read: rc2 [#{rc}]"
155
+ if ZMQ::Util.resultcode_ok?(rc)
156
+ deliver(parts, rc)
157
+ else
158
+ # verify errno corresponds to EAGAIN
159
+ if eagain?
160
+ more = false
161
+ elsif valid_socket_error?
162
+ deliver([], rc)
163
+ end
182
164
  end
183
- #puts "no more parts, ready to deliver"
184
-
185
- # only deliver the messages when rc is 0; otherwise, we
186
- # may have gotten EAGAIN and no message was read;
187
- # don't deliver empty messages
188
- deliver parts, rc if 0 == rc
189
165
  end
190
166
  end
191
167
 
@@ -202,32 +178,8 @@ module ZMQMachine
202
178
 
203
179
  private
204
180
 
205
- def read_message_part parts
206
- message = ZMQ::Message.new
207
- begin
208
- rc = @raw_socket.recv message, ZMQ::NOBLOCK
209
-
210
- if rc
211
- rc = 0 # callers expect 0 for success, not true
212
- parts << message
213
- else
214
- # got EAGAIN most likely
215
- message.close
216
- message = nil
217
- rc = false
218
- end
219
-
220
- rescue ZMQ::ZeroMQError => e
221
- message.close if message
222
- rc = e
223
- end
224
-
225
- rc
226
- end
227
-
228
181
  def deliver parts, rc
229
- #puts "deliver: rc [#{rc}], parts #{parts.inspect}"
230
- if 0 == rc
182
+ if ZMQ::Util.resultcode_ok?(rc)
231
183
  @handler.on_readable self, parts
232
184
  else
233
185
  # this branch is never called
@@ -235,6 +187,21 @@ module ZMQMachine
235
187
  end
236
188
  end
237
189
 
190
+ def eagain?
191
+ ZMQ::EAGAIN == ZMQ::Util.errno
192
+ end
193
+
194
+ def valid_socket_error?
195
+ errno = ZMQ::Util.errno
196
+
197
+ ZMQ::ENOTSUP == errno ||
198
+ ZMQ::EFSM == errno ||
199
+ ZMQ::ETERM == errno ||
200
+ ZMQ::ENOTSOCK == errno ||
201
+ ZMQ::EINTR == errno ||
202
+ ZMQ::EFAULT == errno
203
+ end
204
+
238
205
  end # module Base
239
206
 
240
207
  end # module Socket
@@ -42,7 +42,7 @@ module ZMQMachine
42
42
  include ZMQMachine::Socket::Base
43
43
 
44
44
  def initialize context, handler
45
- @poll_options = ZMQ::POLLIN | ZMQ::POLLOUT
45
+ @poll_options = ZMQ::POLLIN
46
46
  @kind = :pair
47
47
 
48
48
  super
@@ -42,7 +42,7 @@ module ZMQMachine
42
42
  include ZMQMachine::Socket::Base
43
43
 
44
44
  def initialize context, handler
45
- @poll_options = ZMQ::POLLOUT
45
+ @poll_options = 0
46
46
  @kind = :push
47
47
 
48
48
  super
@@ -46,7 +46,6 @@ module ZMQMachine
46
46
  @kind = :reply
47
47
 
48
48
  super
49
- @state = :ready
50
49
  end
51
50
 
52
51
  # Attach a handler to the REP socket.
@@ -66,25 +65,9 @@ module ZMQMachine
66
65
  super
67
66
  end
68
67
 
69
- # +timeout+ is measured in milliseconds; default is 0 (never timeout)
70
- def send_message message
71
- unless waiting_for_request?
72
- rc = super
73
- @state = :waiting_for_request
74
- else
75
- rc = -1
76
- end
77
-
78
- rc
79
- end
80
-
81
68
 
82
69
  private
83
70
 
84
- def waiting_for_request?
85
- :waiting_for_request == @state
86
- end
87
-
88
71
  def allocate_socket context
89
72
  sock = ZMQ::Socket.new context.pointer, ZMQ::REP
90
73
  sock
@@ -42,11 +42,10 @@ module ZMQMachine
42
42
  include ZMQMachine::Socket::Base
43
43
 
44
44
  def initialize context, handler
45
- @poll_options = ZMQ::POLLOUT
45
+ @poll_options = 0
46
46
  @kind = :request
47
47
 
48
48
  super
49
- @state = :ready
50
49
  end
51
50
 
52
51
  # Attach a handler to the REQ socket.
@@ -66,25 +65,9 @@ module ZMQMachine
66
65
  super
67
66
  end
68
67
 
69
- # +timeout+ is measured in milliseconds; default is 0 (never timeout)
70
- def send_message message
71
- unless waiting_for_reply?
72
- rc = super
73
- @state = :waiting_for_reply
74
- else
75
- rc = -1
76
- end
77
-
78
- rc
79
- end
80
-
81
68
 
82
69
  private
83
70
 
84
- def waiting_for_reply?
85
- :waiting_for_reply == @state
86
- end
87
-
88
71
  def allocate_socket context
89
72
  sock = ZMQ::Socket.new context.pointer, ZMQ::REQ
90
73
  sock
@@ -68,7 +68,7 @@ module ZMQMachine
68
68
  end
69
69
 
70
70
  def subscribe topic
71
- @raw_socket.setsockopt ZMQ::SUBSCRIBE, topic
71
+ @raw_socket.setsockopt(ZMQ::SUBSCRIBE, topic)
72
72
  end
73
73
 
74
74
  def subscribe_all
@@ -42,7 +42,7 @@ module ZMQMachine
42
42
  include ZMQMachine::Socket::Base
43
43
 
44
44
  def initialize context, handler
45
- @poll_options = ZMQ::POLLIN | ZMQ::POLLOUT
45
+ @poll_options = ZMQ::POLLIN
46
46
  @kind = :xreply
47
47
 
48
48
  super
@@ -42,7 +42,7 @@ module ZMQMachine
42
42
  include ZMQMachine::Socket::Base
43
43
 
44
44
  def initialize context, handler
45
- @poll_options = ZMQ::POLLIN | ZMQ::POLLOUT
45
+ @poll_options = ZMQ::POLLIN
46
46
  @kind = :xrequest
47
47
 
48
48
  super
data/version.txt CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.6.0
data/zmqmachine.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{zmqmachine}
5
- s.version = "0.5.2"
5
+ s.version = "0.6.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Chuck Remes"]
metadata CHANGED
@@ -1,11 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zmqmachine
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.5.2
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 6
8
+ - 0
9
+ version: 0.6.0
6
10
  platform: ruby
7
11
  authors:
8
- - Chuck Remes
12
+ - Chuck Remes
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
@@ -13,28 +17,36 @@ cert_chain: []
13
17
  date: 2011-07-21 00:00:00 -05:00
14
18
  default_executable:
15
19
  dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: ffi-rzmq
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
20
- none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: 0.7.0
25
- type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: bones
29
- prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
31
- none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: 3.5.4
36
- type: :development
37
- version_requirements: *id002
20
+ - !ruby/object:Gem::Dependency
21
+ name: ffi-rzmq
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 7
31
+ - 0
32
+ version: 0.7.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: bones
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 3
45
+ - 5
46
+ - 4
47
+ version: 3.5.4
48
+ type: :development
49
+ version_requirements: *id002
38
50
  description: |-
39
51
  ZMQMachine is another Ruby implementation of the reactor pattern but this
40
52
  time using 0mq sockets rather than POSIX sockets.
@@ -55,72 +67,76 @@ executables: []
55
67
  extensions: []
56
68
 
57
69
  extra_rdoc_files:
58
- - History.txt
59
- - README.rdoc
60
- - version.txt
70
+ - History.txt
71
+ - README.rdoc
72
+ - version.txt
61
73
  files:
62
- - .bnsignore
63
- - History.txt
64
- - README.rdoc
65
- - Rakefile
66
- - examples/fake_ftp.rb
67
- - examples/one_handed_ping_pong.rb
68
- - examples/ping_pong.rb
69
- - examples/pub_sub.rb
70
- - examples/pubsub_forwarder.rb
71
- - examples/throttled_ping_pong.rb
72
- - lib/zm/address.rb
73
- - lib/zm/deferrable.rb
74
- - lib/zm/devices.rb
75
- - lib/zm/devices/forwarder.rb
76
- - lib/zm/devices/queue.rb
77
- - lib/zm/exceptions.rb
78
- - lib/zm/log_client.rb
79
- - lib/zm/message.rb
80
- - lib/zm/reactor.rb
81
- - lib/zm/sockets.rb
82
- - lib/zm/sockets/base.rb
83
- - lib/zm/sockets/pair.rb
84
- - lib/zm/sockets/pub.rb
85
- - lib/zm/sockets/rep.rb
86
- - lib/zm/sockets/req.rb
87
- - lib/zm/sockets/sub.rb
88
- - lib/zm/sockets/xrep.rb
89
- - lib/zm/sockets/xreq.rb
90
- - lib/zm/sockets/push.rb
91
- - lib/zm/sockets/pull.rb
92
- - lib/zm/timers.rb
93
- - lib/zmqmachine.rb
94
- - spec/spec_helper.rb
95
- - spec/reactor_spec.rb
96
- - version.txt
97
- - zmqmachine.gemspec
74
+ - .bnsignore
75
+ - History.txt
76
+ - README.rdoc
77
+ - Rakefile
78
+ - examples/fake_ftp.rb
79
+ - examples/one_handed_ping_pong.rb
80
+ - examples/ping_pong.rb
81
+ - examples/pub_sub.rb
82
+ - examples/pubsub_forwarder.rb
83
+ - examples/throttled_ping_pong.rb
84
+ - lib/zm/address.rb
85
+ - lib/zm/deferrable.rb
86
+ - lib/zm/devices.rb
87
+ - lib/zm/devices/forwarder.rb
88
+ - lib/zm/devices/queue.rb
89
+ - lib/zm/exceptions.rb
90
+ - lib/zm/log_client.rb
91
+ - lib/zm/message.rb
92
+ - lib/zm/reactor.rb
93
+ - lib/zm/sockets.rb
94
+ - lib/zm/sockets/base.rb
95
+ - lib/zm/sockets/pair.rb
96
+ - lib/zm/sockets/pub.rb
97
+ - lib/zm/sockets/rep.rb
98
+ - lib/zm/sockets/req.rb
99
+ - lib/zm/sockets/sub.rb
100
+ - lib/zm/sockets/xrep.rb
101
+ - lib/zm/sockets/xreq.rb
102
+ - lib/zm/sockets/push.rb
103
+ - lib/zm/sockets/pull.rb
104
+ - lib/zm/timers.rb
105
+ - lib/zmqmachine.rb
106
+ - spec/spec_helper.rb
107
+ - spec/reactor_spec.rb
108
+ - version.txt
109
+ - zmqmachine.gemspec
98
110
  has_rdoc: true
99
111
  homepage: http://github.com/chuckremes/zmqmachine
100
112
  licenses: []
101
113
 
102
114
  post_install_message:
103
115
  rdoc_options:
104
- - --main
105
- - README.rdoc
116
+ - --main
117
+ - README.rdoc
106
118
  require_paths:
107
- - lib
119
+ - lib
108
120
  required_ruby_version: !ruby/object:Gem::Requirement
109
121
  none: false
110
122
  requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: "0"
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ segments:
126
+ - 0
127
+ version: "0"
114
128
  required_rubygems_version: !ruby/object:Gem::Requirement
115
129
  none: false
116
130
  requirements:
117
- - - ">="
118
- - !ruby/object:Gem::Version
119
- version: "0"
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ segments:
134
+ - 0
135
+ version: "0"
120
136
  requirements: []
121
137
 
122
138
  rubyforge_project: zmqmachine
123
- rubygems_version: 1.5.1
139
+ rubygems_version: 1.3.7
124
140
  signing_key:
125
141
  specification_version: 3
126
142
  summary: ZMQMachine is another Ruby implementation of the reactor pattern but this time using 0mq sockets rather than POSIX sockets.