ffi-rzmq 0.9.0 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +3 -0
- data/History.txt +31 -0
- data/README.rdoc +21 -11
- data/Rakefile +2 -34
- data/examples/README.rdoc +1 -1
- data/examples/v2api/latency_measurement.rb +139 -0
- data/examples/v2api/xreqxrep_poll.rb +93 -0
- data/examples/v3api/latency_measurement.rb +139 -0
- data/examples/v3api/xreqxrep_poll.rb +93 -0
- data/ffi-rzmq.gemspec +17 -26
- data/lib/ffi-rzmq/constants.rb +4 -37
- data/lib/ffi-rzmq/libzmq.rb +174 -161
- data/lib/ffi-rzmq/poll.rb +27 -10
- data/lib/ffi-rzmq/socket.rb +116 -85
- data/lib/ffi-rzmq/util.rb +22 -1
- data/lib/ffi-rzmq/version.rb +3 -0
- data/spec/nonblocking_recv_spec.rb +46 -40
- data/spec/poll_spec.rb +84 -0
- data/spec/pushpull_spec.rb +35 -2
- data/spec/socket_spec.rb +0 -2
- data/spec/spec_helper.rb +3 -7
- metadata +117 -95
- data/version.txt +0 -1
data/lib/ffi-rzmq/poll.rb
CHANGED
@@ -85,7 +85,7 @@ module ZMQ
|
|
85
85
|
item[:fd] = fd
|
86
86
|
end
|
87
87
|
|
88
|
-
@raw_to_socket[item
|
88
|
+
@raw_to_socket[item.socket.address] = sock
|
89
89
|
@items << item
|
90
90
|
end
|
91
91
|
|
@@ -143,7 +143,7 @@ module ZMQ
|
|
143
143
|
removed_readable = deregister_readable sock
|
144
144
|
removed_writable = deregister_writable sock
|
145
145
|
|
146
|
-
|
146
|
+
unless (removed_readable || removed_writable) || (size = @sockets.size).zero?
|
147
147
|
@sockets.delete_if { |socket| socket.socket.address == sock.socket.address }
|
148
148
|
socket_deleted = size != @sockets.size
|
149
149
|
|
@@ -172,7 +172,7 @@ module ZMQ
|
|
172
172
|
|
173
173
|
def items_hash hash
|
174
174
|
@items.each do |poll_item|
|
175
|
-
hash[@raw_to_socket[poll_item
|
175
|
+
hash[@raw_to_socket[poll_item.socket.address]] = poll_item
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
@@ -181,8 +181,14 @@ module ZMQ
|
|
181
181
|
@writables.clear
|
182
182
|
|
183
183
|
@items.each do |poll_item|
|
184
|
-
|
185
|
-
|
184
|
+
#FIXME: spec for sockets *and* file descriptors
|
185
|
+
if poll_item.readable?
|
186
|
+
@readables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
187
|
+
end
|
188
|
+
|
189
|
+
if poll_item.writable?
|
190
|
+
@writables << (poll_item.socket.address.zero? ? poll_item.fd : @raw_to_socket[poll_item.socket.address])
|
191
|
+
end
|
186
192
|
end
|
187
193
|
end
|
188
194
|
|
@@ -194,11 +200,22 @@ module ZMQ
|
|
194
200
|
# Users will pass in values measured as
|
195
201
|
# milliseconds, so we need to convert that value to
|
196
202
|
# microseconds for the library.
|
197
|
-
|
198
|
-
|
199
|
-
-1
|
200
|
-
|
201
|
-
|
203
|
+
if LibZMQ.version2?
|
204
|
+
def adjust timeout
|
205
|
+
if :blocking == timeout || -1 == timeout
|
206
|
+
-1
|
207
|
+
else
|
208
|
+
(timeout * 1000).to_i
|
209
|
+
end
|
210
|
+
end
|
211
|
+
else
|
212
|
+
# version3 changed units from microseconds to milliseconds
|
213
|
+
def adjust timeout
|
214
|
+
if :blocking == timeout || -1 == timeout
|
215
|
+
-1
|
216
|
+
else
|
217
|
+
timeout.to_i
|
218
|
+
end
|
202
219
|
end
|
203
220
|
end
|
204
221
|
end
|
data/lib/ffi-rzmq/socket.rb
CHANGED
@@ -34,7 +34,7 @@ module ZMQ
|
|
34
34
|
def self.create context_ptr, type, opts = {:receiver_class => ZMQ::Message}
|
35
35
|
new(context_ptr, type, opts) rescue nil
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# To avoid rescuing exceptions, use the factory method #create for
|
39
39
|
# all socket creation.
|
40
40
|
#
|
@@ -68,7 +68,7 @@ module ZMQ
|
|
68
68
|
# users may override the classes used for receiving; class must conform to the
|
69
69
|
# same public API as ZMQ::Message
|
70
70
|
@receiver_klass = opts[:receiver_class]
|
71
|
-
|
71
|
+
|
72
72
|
context_ptr = context_ptr.pointer if context_ptr.kind_of?(ZMQ::Context)
|
73
73
|
|
74
74
|
unless context_ptr.null?
|
@@ -100,14 +100,13 @@ module ZMQ
|
|
100
100
|
# ZMQ::RECONNECT_IVL
|
101
101
|
# ZMQ::BACKLOG
|
102
102
|
# ZMQ::RECOVER_IVL_MSEC (version 2 only)
|
103
|
-
# ZMQ::RECONNECT_IVL_MAX (version 3
|
104
|
-
# ZMQ::MAXMSGSIZE (version 3
|
105
|
-
# ZMQ::SNDHWM (version 3
|
106
|
-
# ZMQ::RCVHWM (version 3
|
107
|
-
# ZMQ::MULTICAST_HOPS (version 3
|
108
|
-
# ZMQ::RCVTIMEO (version 3
|
109
|
-
# ZMQ::SNDTIMEO (version 3
|
110
|
-
# ZMQ::RCVLABEL (version 3/4 only)
|
103
|
+
# ZMQ::RECONNECT_IVL_MAX (version 3 only)
|
104
|
+
# ZMQ::MAXMSGSIZE (version 3 only)
|
105
|
+
# ZMQ::SNDHWM (version 3 only)
|
106
|
+
# ZMQ::RCVHWM (version 3 only)
|
107
|
+
# ZMQ::MULTICAST_HOPS (version 3 only)
|
108
|
+
# ZMQ::RCVTIMEO (version 3 only)
|
109
|
+
# ZMQ::SNDTIMEO (version 3 only)
|
111
110
|
#
|
112
111
|
# Valid +name+ values that take a string +value+ are:
|
113
112
|
# ZMQ::IDENTITY (version 2/3 only)
|
@@ -169,7 +168,7 @@ module ZMQ
|
|
169
168
|
def more_parts?
|
170
169
|
array = []
|
171
170
|
rc = getsockopt ZMQ::RCVMORE, array
|
172
|
-
|
171
|
+
|
173
172
|
Util.resultcode_ok?(rc) ? array.at(0) : false
|
174
173
|
end
|
175
174
|
|
@@ -197,7 +196,7 @@ module ZMQ
|
|
197
196
|
#
|
198
197
|
# rc = socket.close
|
199
198
|
# puts("Given socket was invalid!") unless 0 == rc
|
200
|
-
#
|
199
|
+
#
|
201
200
|
def close
|
202
201
|
if @socket
|
203
202
|
remove_finalizer
|
@@ -244,7 +243,7 @@ module ZMQ
|
|
244
243
|
length.write_int 8
|
245
244
|
@sockopt_cache[:int64] = [FFI::MemoryPointer.new(:int64), length]
|
246
245
|
end
|
247
|
-
|
246
|
+
|
248
247
|
@sockopt_cache[:int64]
|
249
248
|
|
250
249
|
elsif int_option?(name)
|
@@ -254,7 +253,7 @@ module ZMQ
|
|
254
253
|
length.write_int 4
|
255
254
|
@sockopt_cache[:int32] = [FFI::MemoryPointer.new(:int32), length]
|
256
255
|
end
|
257
|
-
|
256
|
+
|
258
257
|
@sockopt_cache[:int32]
|
259
258
|
|
260
259
|
elsif string_option?(name)
|
@@ -262,7 +261,7 @@ module ZMQ
|
|
262
261
|
# could be a string of up to 255 bytes
|
263
262
|
length.write_int 255
|
264
263
|
[FFI::MemoryPointer.new(255), length]
|
265
|
-
|
264
|
+
|
266
265
|
else
|
267
266
|
# uh oh, someone passed in an unknown option; use a slop buffer
|
268
267
|
unless @sockopt_cache[:unknown]
|
@@ -270,7 +269,7 @@ module ZMQ
|
|
270
269
|
length.write_int 4
|
271
270
|
@sockopt_cache[:unknown] = [FFI::MemoryPointer.new(:int32), length]
|
272
271
|
end
|
273
|
-
|
272
|
+
|
274
273
|
@sockopt_cache[:unknown]
|
275
274
|
end
|
276
275
|
end
|
@@ -386,12 +385,12 @@ module ZMQ
|
|
386
385
|
#
|
387
386
|
def getsockopt name, array
|
388
387
|
rc = __getsockopt__ name, array
|
389
|
-
|
388
|
+
|
390
389
|
if Util.resultcode_ok?(rc) && (RCVMORE == name || MCAST_LOOP == name)
|
391
390
|
# convert to boolean
|
392
391
|
array[0] = 1 == array[0]
|
393
392
|
end
|
394
|
-
|
393
|
+
|
395
394
|
rc
|
396
395
|
end
|
397
396
|
|
@@ -564,14 +563,14 @@ module ZMQ
|
|
564
563
|
def recv_strings list, flag = 0
|
565
564
|
array = []
|
566
565
|
rc = recvmsgs array, flag
|
567
|
-
|
566
|
+
|
568
567
|
if Util.resultcode_ok?(rc)
|
569
568
|
array.each do |message|
|
570
569
|
list << message.copy_out_string
|
571
570
|
message.close
|
572
571
|
end
|
573
572
|
end
|
574
|
-
|
573
|
+
|
575
574
|
rc
|
576
575
|
end
|
577
576
|
|
@@ -594,26 +593,60 @@ module ZMQ
|
|
594
593
|
def recvmsgs list, flag = 0
|
595
594
|
flag = NOBLOCK if noblock?(flag)
|
596
595
|
|
597
|
-
parts = []
|
598
596
|
message = @receiver_klass.new
|
599
597
|
rc = recv message, flag
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
rc
|
608
|
-
|
598
|
+
|
599
|
+
if Util.resultcode_ok?(rc)
|
600
|
+
list << message
|
601
|
+
|
602
|
+
# check rc *first*; necessary because the call to #more_parts? can reset
|
603
|
+
# the zmq_errno to a weird value, so the zmq_errno that was set on the
|
604
|
+
# call to #recv gets lost
|
605
|
+
while Util.resultcode_ok?(rc) && more_parts?
|
606
|
+
message = @receiver_klass.new
|
607
|
+
rc = recv message, flag
|
608
|
+
|
609
|
+
if Util.resultcode_ok?(rc)
|
610
|
+
list << message
|
611
|
+
else
|
612
|
+
message.close
|
613
|
+
list.each { |msg| msg.close }
|
614
|
+
list.clear
|
615
|
+
end
|
616
|
+
end
|
617
|
+
else
|
618
|
+
message.close
|
609
619
|
end
|
610
620
|
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
621
|
+
rc
|
622
|
+
end
|
623
|
+
|
624
|
+
# Should only be used for XREQ, XREP, DEALER and ROUTER type sockets. Takes
|
625
|
+
# a +list+ for receiving the message body parts and a +routing_envelope+
|
626
|
+
# for receiving the message parts comprising the 0mq routing information.
|
627
|
+
#
|
628
|
+
# Returns 0 when all messages were successfully dequeued.
|
629
|
+
# Returns -1 under two conditions.
|
630
|
+
# 1. A message could not be dequeued
|
631
|
+
# 2. When +flags+ is set with ZMQ::NOBLOCK and the socket returned EAGAIN.
|
632
|
+
#
|
633
|
+
# With a -1 return code, the user must check ZMQ.errno to determine the
|
634
|
+
# cause. Also, the +list+ *may* be modified when there was an error.
|
635
|
+
#
|
636
|
+
def recv_multipart list, routing_envelope, flag = 0
|
637
|
+
parts = []
|
638
|
+
rc = recvmsgs parts, flag
|
639
|
+
|
615
640
|
if Util.resultcode_ok?(rc)
|
616
|
-
|
641
|
+
routing = true
|
642
|
+
parts.each do |part|
|
643
|
+
if routing
|
644
|
+
routing_envelope << part
|
645
|
+
routing = part.size > 0
|
646
|
+
else
|
647
|
+
list << part
|
648
|
+
end
|
649
|
+
end
|
617
650
|
end
|
618
651
|
|
619
652
|
rc
|
@@ -662,7 +695,7 @@ module ZMQ
|
|
662
695
|
end # LibZMQ.version2?
|
663
696
|
|
664
697
|
|
665
|
-
if LibZMQ.version3?
|
698
|
+
if LibZMQ.version3?
|
666
699
|
class Socket
|
667
700
|
include CommonSocketBehavior
|
668
701
|
include IdentitySupport
|
@@ -704,41 +737,13 @@ module ZMQ
|
|
704
737
|
#
|
705
738
|
def getsockopt name, array
|
706
739
|
rc = __getsockopt__ name, array
|
707
|
-
|
740
|
+
|
708
741
|
if Util.resultcode_ok?(rc) && (RCVMORE == name)
|
709
742
|
# convert to boolean
|
710
743
|
array[0] = 1 == array[0]
|
711
744
|
end
|
712
|
-
|
713
|
-
rc
|
714
|
-
end
|
715
745
|
|
716
|
-
|
717
|
-
#
|
718
|
-
# Equivalent to calling Socket#getsockopt with ZMQ::RCVLABEL.
|
719
|
-
#
|
720
|
-
# Warning: if the call to #getsockopt fails, this method will return
|
721
|
-
# false and swallow the error.
|
722
|
-
#
|
723
|
-
# labels = []
|
724
|
-
# message_parts = []
|
725
|
-
# message = Message.new
|
726
|
-
# rc = socket.recv(message)
|
727
|
-
# if ZMQ::Util.resultcode_ok?(rc)
|
728
|
-
# label? ? labels.push(message) : message_parts.push(message)
|
729
|
-
# while more_parts?
|
730
|
-
# message = Message.new
|
731
|
-
# if ZMQ::Util.resulcode_ok?(socket.recv(message))
|
732
|
-
# label? ? labels.push(message) : message_parts.push(message)
|
733
|
-
# end
|
734
|
-
# end
|
735
|
-
# end
|
736
|
-
#
|
737
|
-
def label?
|
738
|
-
array = []
|
739
|
-
rc = getsockopt ZMQ::RCVLABEL, array
|
740
|
-
|
741
|
-
Util.resultcode_ok?(rc) ? array.at(0) : false
|
746
|
+
rc
|
742
747
|
end
|
743
748
|
|
744
749
|
# Queues the message for transmission. Message is assumed to conform to the
|
@@ -796,7 +801,7 @@ module ZMQ
|
|
796
801
|
def send_strings parts, flags = 0
|
797
802
|
return -1 if !parts || parts.empty?
|
798
803
|
flags = DONTWAIT if dontwait?(flags)
|
799
|
-
|
804
|
+
|
800
805
|
parts[0..-2].each do |part|
|
801
806
|
rc = send_string part, (flags | ZMQ::SNDMORE)
|
802
807
|
return rc unless Util.resultcode_ok?(rc)
|
@@ -822,7 +827,7 @@ module ZMQ
|
|
822
827
|
def sendmsgs parts, flags = 0
|
823
828
|
return -1 if !parts || parts.empty?
|
824
829
|
flags = DONTWAIT if dontwait?(flags)
|
825
|
-
|
830
|
+
|
826
831
|
parts[0..-2].each do |part|
|
827
832
|
rc = sendmsg part, (flags | ZMQ::SNDMORE)
|
828
833
|
return rc unless Util.resultcode_ok?(rc)
|
@@ -901,14 +906,14 @@ module ZMQ
|
|
901
906
|
def recv_strings list, flag = 0
|
902
907
|
array = []
|
903
908
|
rc = recvmsgs array, flag
|
904
|
-
|
909
|
+
|
905
910
|
if Util.resultcode_ok?(rc)
|
906
911
|
array.each do |message|
|
907
912
|
list << message.copy_out_string
|
908
913
|
message.close
|
909
914
|
end
|
910
915
|
end
|
911
|
-
|
916
|
+
|
912
917
|
rc
|
913
918
|
end
|
914
919
|
|
@@ -918,28 +923,55 @@ module ZMQ
|
|
918
923
|
# +flag+ may be ZMQ::DONTWAIT. Any other flag will be
|
919
924
|
# removed.
|
920
925
|
#
|
921
|
-
# Raises the same exceptions as Socket#recv.
|
922
|
-
#
|
923
926
|
def recvmsgs list, flag = 0
|
924
927
|
flag = DONTWAIT if dontwait?(flag)
|
925
928
|
|
926
|
-
parts = []
|
927
929
|
message = @receiver_klass.new
|
928
930
|
rc = recvmsg message, flag
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
rc
|
937
|
-
|
931
|
+
|
932
|
+
if Util.resultcode_ok?(rc)
|
933
|
+
list << message
|
934
|
+
|
935
|
+
# check rc *first*; necessary because the call to #more_parts? can reset
|
936
|
+
# the zmq_errno to a weird value, so the zmq_errno that was set on the
|
937
|
+
# call to #recv gets lost
|
938
|
+
while Util.resultcode_ok?(rc) && more_parts?
|
939
|
+
message = @receiver_klass.new
|
940
|
+
rc = recvmsg message, flag
|
941
|
+
|
942
|
+
if Util.resultcode_ok?(rc)
|
943
|
+
list << message
|
944
|
+
else
|
945
|
+
message.close
|
946
|
+
list.each { |msg| msg.close }
|
947
|
+
list.clear
|
948
|
+
end
|
949
|
+
end
|
950
|
+
else
|
951
|
+
message.close
|
938
952
|
end
|
939
953
|
|
940
|
-
|
954
|
+
rc
|
955
|
+
end
|
956
|
+
|
957
|
+
# Should only be used for XREQ, XREP, DEALER and ROUTER type sockets. Takes
|
958
|
+
# a +list+ for receiving the message body parts and a +routing_envelope+
|
959
|
+
# for receiving the message parts comprising the 0mq routing information.
|
960
|
+
#
|
961
|
+
def recv_multipart list, routing_envelope, flag = 0
|
962
|
+
parts = []
|
963
|
+
rc = recvmsgs parts, flag
|
964
|
+
|
941
965
|
if Util.resultcode_ok?(rc)
|
942
|
-
|
966
|
+
routing = true
|
967
|
+
parts.each do |part|
|
968
|
+
if routing
|
969
|
+
routing_envelope << part
|
970
|
+
routing = part.size > 0
|
971
|
+
else
|
972
|
+
list << part
|
973
|
+
end
|
974
|
+
end
|
943
975
|
end
|
944
976
|
|
945
977
|
rc
|
@@ -955,7 +987,6 @@ module ZMQ
|
|
955
987
|
|
956
988
|
def int_option? name
|
957
989
|
super ||
|
958
|
-
RCVLABEL == name ||
|
959
990
|
RECONNECT_IVL_MAX == name ||
|
960
991
|
RCVHWM == name ||
|
961
992
|
SNDHWM == name ||
|
data/lib/ffi-rzmq/util.rb
CHANGED
@@ -42,6 +42,22 @@ module ZMQ
|
|
42
42
|
[major.read_int, minor.read_int, patch.read_int]
|
43
43
|
end
|
44
44
|
|
45
|
+
# Attempts to bind to a random tcp port on +host+ up to +max_tries+
|
46
|
+
# times. Returns the port number upon success or nil upon failure.
|
47
|
+
#
|
48
|
+
def self.bind_to_random_tcp_port host = '127.0.0.1', max_tries = 500
|
49
|
+
tries = 0
|
50
|
+
rc = -1
|
51
|
+
|
52
|
+
while !resultcode_ok?(rc) && tries < max_tries
|
53
|
+
tries += 1
|
54
|
+
random = random_port
|
55
|
+
rc = socket.bind "tcp://#{host}:#{random}"
|
56
|
+
end
|
57
|
+
|
58
|
+
resultcode_ok?(rc) ? random : nil
|
59
|
+
end
|
60
|
+
|
45
61
|
# Returns the proper flag value for non-blocking regardless of 0mq
|
46
62
|
# version.
|
47
63
|
#
|
@@ -51,7 +67,7 @@ module ZMQ
|
|
51
67
|
NOBLOCK
|
52
68
|
end
|
53
69
|
|
54
|
-
elsif LibZMQ.version3?
|
70
|
+
elsif LibZMQ.version3?
|
55
71
|
|
56
72
|
def self.nonblocking_flag
|
57
73
|
DONTWAIT
|
@@ -62,6 +78,11 @@ module ZMQ
|
|
62
78
|
|
63
79
|
private
|
64
80
|
|
81
|
+
# generate a random port between 10_000 and 65534
|
82
|
+
def self.random_port
|
83
|
+
rand(55534) + 10_000
|
84
|
+
end
|
85
|
+
|
65
86
|
# :doc:
|
66
87
|
# Called by most library methods to verify there were no errors during
|
67
88
|
# operation. If any are found, raise the appropriate #ZeroMQError.
|