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 +9 -0
- data/README.rdoc +5 -5
- data/examples/pubsub_forwarder.rb +5 -1
- data/lib/zm/address.rb +23 -5
- data/lib/zm/devices/forwarder.rb +35 -8
- data/lib/zm/devices/queue.rb +28 -6
- data/lib/zm/exceptions.rb +4 -0
- data/lib/zm/log_client.rb +21 -9
- data/lib/zm/reactor.rb +53 -37
- data/lib/zm/sockets/base.rb +71 -104
- data/lib/zm/sockets/pair.rb +1 -1
- data/lib/zm/sockets/push.rb +1 -1
- data/lib/zm/sockets/rep.rb +0 -17
- data/lib/zm/sockets/req.rb +1 -18
- data/lib/zm/sockets/sub.rb +1 -1
- data/lib/zm/sockets/xrep.rb +1 -1
- data/lib/zm/sockets/xreq.rb +1 -1
- data/version.txt +1 -1
- data/zmqmachine.gemspec +1 -1
- metadata +90 -74
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
|
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.
|
44
|
+
* 0mq 2.1.x, 3.0 and later
|
45
45
|
|
46
46
|
Depends on 2 external gems.
|
47
47
|
|
48
|
-
* ffi-rzmq (>= 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 =
|
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
|
-
#
|
40
|
-
#
|
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.
|
92
|
+
Address.create host, port, type.downcase.to_sym
|
75
93
|
end
|
76
94
|
|
77
95
|
private
|
data/lib/zm/devices/forwarder.rb
CHANGED
@@ -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
|
99
|
-
|
100
|
-
|
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
|
|
data/lib/zm/devices/queue.rb
CHANGED
@@ -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
|
-
|
100
|
-
|
101
|
-
|
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
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
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
479
|
-
|
480
|
-
|
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
|
-
|
525
|
-
|
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
|
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
|
|
data/lib/zm/sockets/base.rb
CHANGED
@@ -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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
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
|
-
|
109
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
155
|
-
#
|
156
|
-
def identity() @raw_socket.identity; end
|
132
|
+
if LibZMQ.version2? || LibZMQ.version3?
|
157
133
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
173
|
-
while
|
149
|
+
more = true
|
150
|
+
|
151
|
+
while ZMQ::Util.resultcode_ok?(rc) && more
|
174
152
|
parts = []
|
175
|
-
rc =
|
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
|
-
|
179
|
-
|
180
|
-
|
181
|
-
#
|
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
|
-
|
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
|
data/lib/zm/sockets/pair.rb
CHANGED
data/lib/zm/sockets/push.rb
CHANGED
data/lib/zm/sockets/rep.rb
CHANGED
@@ -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
|
data/lib/zm/sockets/req.rb
CHANGED
@@ -42,11 +42,10 @@ module ZMQMachine
|
|
42
42
|
include ZMQMachine::Socket::Base
|
43
43
|
|
44
44
|
def initialize context, handler
|
45
|
-
@poll_options =
|
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
|
data/lib/zm/sockets/sub.rb
CHANGED
data/lib/zm/sockets/xrep.rb
CHANGED
data/lib/zm/sockets/xreq.rb
CHANGED
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/zmqmachine.gemspec
CHANGED
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
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 6
|
8
|
+
- 0
|
9
|
+
version: 0.6.0
|
6
10
|
platform: ruby
|
7
11
|
authors:
|
8
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
70
|
+
- History.txt
|
71
|
+
- README.rdoc
|
72
|
+
- version.txt
|
61
73
|
files:
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
105
|
-
|
116
|
+
- --main
|
117
|
+
- README.rdoc
|
106
118
|
require_paths:
|
107
|
-
|
119
|
+
- lib
|
108
120
|
required_ruby_version: !ruby/object:Gem::Requirement
|
109
121
|
none: false
|
110
122
|
requirements:
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
119
|
-
|
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.
|
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.
|