ffi-rzmq 0.9.0 → 0.9.2
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/.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.
|