zmqmachine 0.5.2 → 0.6.0

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/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.