ffi-rzmq 0.8.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS.txt +1 -0
- data/History.txt +35 -0
- data/README.rdoc +48 -15
- data/Rakefile +7 -2
- data/examples/README.rdoc +21 -76
- data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
- data/examples/v2api/local_lat_poll.rb +66 -0
- data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
- data/examples/v2api/publish_subscribe.rb +82 -0
- data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
- data/examples/v2api/remote_throughput.rb +39 -0
- data/examples/v2api/reqrep_poll.rb +62 -0
- data/examples/v2api/request_response.rb +40 -0
- data/examples/v2api/throughput_measurement.rb +138 -0
- data/examples/v3api/local_lat.rb +59 -0
- data/examples/v3api/local_lat_poll.rb +66 -0
- data/examples/v3api/local_throughput.rb +65 -0
- data/examples/v3api/publish_subscribe.rb +82 -0
- data/examples/v3api/remote_lat.rb +71 -0
- data/examples/v3api/remote_throughput.rb +47 -0
- data/examples/v3api/reqrep_poll.rb +62 -0
- data/examples/v3api/request_response.rb +40 -0
- data/examples/v3api/throughput_measurement.rb +166 -0
- data/ext/README +5 -0
- data/ffi-rzmq.gemspec +4 -4
- data/lib/ffi-rzmq.rb +4 -1
- data/lib/ffi-rzmq/constants.rb +178 -0
- data/lib/ffi-rzmq/context.rb +61 -45
- data/lib/ffi-rzmq/device.rb +22 -9
- data/lib/ffi-rzmq/exceptions.rb +0 -98
- data/lib/ffi-rzmq/libc.rb +19 -0
- data/lib/ffi-rzmq/libzmq.rb +188 -0
- data/lib/ffi-rzmq/message.rb +33 -40
- data/lib/ffi-rzmq/poll.rb +49 -52
- data/lib/ffi-rzmq/socket.rb +902 -392
- data/lib/ffi-rzmq/util.rb +101 -0
- data/spec/context_spec.rb +47 -21
- data/spec/device_spec.rb +78 -58
- data/spec/message_spec.rb +90 -12
- data/spec/multipart_spec.rb +162 -0
- data/spec/nonblocking_recv_spec.rb +325 -0
- data/spec/pushpull_spec.rb +95 -34
- data/spec/reqrep_spec.rb +55 -20
- data/spec/socket_spec.rb +353 -204
- data/spec/spec_helper.rb +46 -3
- data/version.txt +1 -1
- metadata +91 -66
- data/examples/local_lat_poll.rb +0 -54
- data/examples/local_lat_zerocopy.rb +0 -24
- data/examples/publish_subscribe.rb +0 -52
- data/examples/remote_lat_zerocopy.rb +0 -35
- data/examples/remote_throughput.rb +0 -27
- data/examples/reqrep_poll.rb +0 -49
- data/examples/request_response.rb +0 -23
- data/lib/ffi-rzmq/wrapper.rb +0 -121
- data/lib/ffi-rzmq/zmq.rb +0 -198
data/spec/pushpull_spec.rb
CHANGED
@@ -13,55 +13,116 @@ module ZMQ
|
|
13
13
|
@context = ZMQ::Context.new
|
14
14
|
@push = @context.socket ZMQ::PUSH
|
15
15
|
@pull = @context.socket ZMQ::PULL
|
16
|
-
@
|
17
|
-
@pull.
|
16
|
+
@push.setsockopt ZMQ::LINGER, 0
|
17
|
+
@pull.setsockopt ZMQ::LINGER, 0
|
18
|
+
port = connect_to_random_tcp_port(@pull)
|
19
|
+
@link = "tcp://127.0.0.1:#{port}"
|
18
20
|
@push.bind @link
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
after(:each) do
|
22
24
|
@push.close
|
23
25
|
@pull.close
|
26
|
+
@context.terminate
|
24
27
|
end
|
25
|
-
|
28
|
+
|
26
29
|
it "should receive an exact copy of the sent message using Message objects directly on one pull socket" do
|
27
30
|
@push.send_string string
|
28
|
-
received =
|
31
|
+
received = ''
|
32
|
+
rc = @pull.recv_string received
|
29
33
|
received.should == string
|
30
34
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
|
36
|
+
if version2?
|
37
|
+
it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
|
38
|
+
sent_message = Message.new string
|
39
|
+
received_message = Message.new
|
40
|
+
|
41
|
+
rc = @push.send sent_message
|
42
|
+
rc.should == 0
|
43
|
+
sleep 0.1 # give it time for delivery
|
44
|
+
rc = @pull.recv received_message, ZMQ::NOBLOCK
|
45
|
+
received_message.copy_out_string.should == string
|
46
|
+
end
|
47
|
+
|
48
|
+
else
|
49
|
+
|
50
|
+
it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
|
51
|
+
sent_message = Message.new string
|
52
|
+
received_message = Message.new
|
53
|
+
|
54
|
+
rc = @push.sendmsg sent_message
|
55
|
+
rc.should == string.size
|
56
|
+
sleep 0.1 # give it time for delivery
|
57
|
+
rc = @pull.recvmsg received_message, ZMQ::DONTWAIT
|
58
|
+
rc.should == string.size
|
59
|
+
received_message.copy_out_string.should == string
|
60
|
+
end
|
40
61
|
end
|
41
62
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
|
64
|
+
if version2?
|
65
|
+
|
66
|
+
it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages" do
|
67
|
+
received = []
|
68
|
+
threads = []
|
69
|
+
count = 4
|
70
|
+
@pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
|
71
|
+
|
72
|
+
count.times do |i|
|
73
|
+
threads << Thread.new do
|
74
|
+
pull = @context.socket ZMQ::PULL
|
75
|
+
rc = pull.setsockopt ZMQ::LINGER, 0
|
76
|
+
rc = pull.connect @link
|
77
|
+
rc.should == 0
|
78
|
+
buffer = ''
|
79
|
+
rc = pull.recv_string buffer
|
80
|
+
rc.should == 0
|
81
|
+
received << buffer
|
82
|
+
pull.close
|
83
|
+
end
|
84
|
+
sleep 0.01 # give each thread time to spin up
|
54
85
|
end
|
55
|
-
|
86
|
+
|
87
|
+
count.times { @push.send_string(string) }
|
88
|
+
|
89
|
+
threads.each {|t| t.join}
|
90
|
+
|
91
|
+
received.find_all {|r| r == string}.length.should == count
|
56
92
|
end
|
57
|
-
|
58
|
-
count.times { @push.send_string(string) }
|
59
93
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
94
|
+
else # version3 or 4
|
95
|
+
|
96
|
+
it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages" do
|
97
|
+
received = []
|
98
|
+
threads = []
|
99
|
+
count = 4
|
100
|
+
@pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
|
101
|
+
|
102
|
+
count.times do |i|
|
103
|
+
threads << Thread.new do
|
104
|
+
pull = @context.socket ZMQ::PULL
|
105
|
+
rc = pull.setsockopt ZMQ::LINGER, 0
|
106
|
+
rc = pull.connect @link
|
107
|
+
rc.should == 0
|
108
|
+
buffer = ''
|
109
|
+
rc = pull.recv_string buffer
|
110
|
+
rc.should == string.size
|
111
|
+
received << buffer
|
112
|
+
pull.close
|
113
|
+
end
|
114
|
+
sleep 0.01 # give each thread time to spin up
|
115
|
+
end
|
116
|
+
|
117
|
+
count.times { @push.send_string(string) }
|
118
|
+
|
119
|
+
threads.each {|t| t.join}
|
120
|
+
|
121
|
+
received.find_all {|r| r == string}.length.should == count
|
122
|
+
end
|
123
|
+
|
124
|
+
end # if version...
|
125
|
+
|
65
126
|
end # @context ping-pong
|
66
127
|
end # describe
|
67
128
|
end # module ZMQ
|
data/spec/reqrep_spec.rb
CHANGED
@@ -15,11 +15,10 @@ module ZMQ
|
|
15
15
|
context = ZMQ::Context.new 1
|
16
16
|
@ping = context.socket ZMQ::REQ
|
17
17
|
@pong = context.socket ZMQ::REP
|
18
|
-
|
19
|
-
@
|
20
|
-
@ping.connect link
|
18
|
+
port = bind_to_random_tcp_port(@pong)
|
19
|
+
@ping.connect "tcp://127.0.0.1:#{port}"
|
21
20
|
end
|
22
|
-
|
21
|
+
|
23
22
|
after(:each) do
|
24
23
|
@ping.close
|
25
24
|
@pong.close
|
@@ -27,31 +26,67 @@ module ZMQ
|
|
27
26
|
|
28
27
|
it "should receive an exact string copy of the string message sent" do
|
29
28
|
@ping.send_string string
|
30
|
-
received_message =
|
29
|
+
received_message = ''
|
30
|
+
rc = @pong.recv_string received_message
|
31
31
|
|
32
32
|
received_message.should == string
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
if version2?
|
36
|
+
it "should receive an exact copy of the sent message using Message objects directly" do
|
37
|
+
sent_message = Message.new string
|
38
|
+
received_message = Message.new
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
rc = @ping.send sent_message
|
41
|
+
rc.should == 0
|
42
|
+
rc = @pong.recv received_message
|
43
|
+
rc.should == 0
|
41
44
|
|
42
|
-
|
43
|
-
|
45
|
+
received_message.copy_out_string.should == string
|
46
|
+
end
|
44
47
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
+
it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
|
49
|
+
sent_message = Message.new string
|
50
|
+
received_message = Message.new
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
rc = @ping.send sent_message, ZMQ::NOBLOCK
|
53
|
+
rc.should == 0
|
54
|
+
sleep 0.1 # give it time for delivery
|
55
|
+
rc = @pong.recv received_message, ZMQ::NOBLOCK
|
56
|
+
rc.should == 0
|
57
|
+
|
58
|
+
received_message.copy_out_string.should == string
|
59
|
+
end
|
60
|
+
|
61
|
+
else # version3 or 4
|
62
|
+
|
63
|
+
it "should receive an exact copy of the sent message using Message objects directly" do
|
64
|
+
sent_message = Message.new string
|
65
|
+
received_message = Message.new
|
66
|
+
|
67
|
+
rc = @ping.sendmsg sent_message
|
68
|
+
rc.should == string.size
|
69
|
+
rc = @pong.recvmsg received_message
|
70
|
+
rc.should == string.size
|
71
|
+
|
72
|
+
received_message.copy_out_string.should == string
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
|
76
|
+
sent_message = Message.new string
|
77
|
+
received_message = Message.new
|
78
|
+
|
79
|
+
rc = @ping.sendmsg sent_message, ZMQ::DONTWAIT
|
80
|
+
rc.should == string.size
|
81
|
+
sleep 0.001 # give it time for delivery
|
82
|
+
rc = @pong.recvmsg received_message, ZMQ::DONTWAIT
|
83
|
+
rc.should == string.size
|
84
|
+
|
85
|
+
received_message.copy_out_string.should == string
|
86
|
+
end
|
87
|
+
|
88
|
+
end # if version...
|
52
89
|
|
53
|
-
received_message.copy_out_string.should == string
|
54
|
-
end
|
55
90
|
end # context ping-pong
|
56
91
|
|
57
92
|
|
data/spec/socket_spec.rb
CHANGED
@@ -6,6 +6,14 @@ module ZMQ
|
|
6
6
|
|
7
7
|
describe Socket do
|
8
8
|
|
9
|
+
socket_types = if LibZMQ.version2?
|
10
|
+
[ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR]
|
11
|
+
elsif LibZMQ.version3?
|
12
|
+
[ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR, ZMQ::XPUB, ZMQ::XSUB]
|
13
|
+
elsif LibZMQ.version4?
|
14
|
+
[ZMQ::REQ, ZMQ::REP, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR, ZMQ::XPUB, ZMQ::XSUB]
|
15
|
+
end
|
16
|
+
|
9
17
|
context "when initializing" do
|
10
18
|
before(:all) { @ctx = Context.new }
|
11
19
|
after(:all) { @ctx.terminate }
|
@@ -15,9 +23,24 @@ module ZMQ
|
|
15
23
|
lambda { Socket.new(FFI::Pointer.new(0), ZMQ::REQ) }.should raise_exception(ZMQ::ContextError)
|
16
24
|
end
|
17
25
|
|
18
|
-
|
26
|
+
it "works with a Context#pointer as the context_ptr" do
|
27
|
+
lambda do
|
28
|
+
s = Socket.new(@ctx.pointer, ZMQ::REQ)
|
29
|
+
s.close
|
30
|
+
end.should_not raise_exception(ZMQ::ContextError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "works with a Context instance as the context_ptr" do
|
34
|
+
lambda do
|
35
|
+
s = Socket.new(@ctx, ZMQ::SUB)
|
36
|
+
s.close
|
37
|
+
end.should_not raise_exception(ZMQ::ContextError)
|
38
|
+
end
|
39
|
+
|
19
40
|
|
20
|
-
|
41
|
+
socket_types.each do |socket_type|
|
42
|
+
|
43
|
+
it "should not raise an error for a [#{ZMQ::SocketTypeNameMap[socket_type]}] socket type" do
|
21
44
|
sock = nil
|
22
45
|
lambda { sock = Socket.new(@ctx.pointer, socket_type) }.should_not raise_error
|
23
46
|
sock.close
|
@@ -39,8 +62,8 @@ module ZMQ
|
|
39
62
|
sock.close
|
40
63
|
end
|
41
64
|
end # context initializing
|
42
|
-
|
43
|
-
|
65
|
+
|
66
|
+
|
44
67
|
context "calling close" do
|
45
68
|
before(:all) { @ctx = Context.new }
|
46
69
|
after(:all) { @ctx.terminate }
|
@@ -57,49 +80,57 @@ module ZMQ
|
|
57
80
|
end # context calling close
|
58
81
|
|
59
82
|
|
60
|
-
|
61
|
-
before(:all) { @ctx = Context.new }
|
62
|
-
after(:all) { @ctx.terminate }
|
83
|
+
if version2? || version3?
|
63
84
|
|
64
|
-
|
65
|
-
|
85
|
+
context "identity=" do
|
86
|
+
before(:all) { @ctx = Context.new }
|
87
|
+
after(:all) { @ctx.terminate }
|
66
88
|
|
67
|
-
|
68
|
-
|
69
|
-
end
|
89
|
+
it "fails to set identity for identities in excess of 255 bytes" do
|
90
|
+
sock = Socket.new @ctx.pointer, ZMQ::REQ
|
70
91
|
|
71
|
-
|
72
|
-
|
92
|
+
sock.identity = ('a' * 256)
|
93
|
+
sock.identity.should == ''
|
94
|
+
sock.close
|
95
|
+
end
|
73
96
|
|
74
|
-
|
75
|
-
|
76
|
-
end
|
97
|
+
it "fails to set identity for identities of length 0" do
|
98
|
+
sock = Socket.new @ctx.pointer, ZMQ::REQ
|
77
99
|
|
78
|
-
|
79
|
-
|
100
|
+
sock.identity = ''
|
101
|
+
sock.identity.should == ''
|
102
|
+
sock.close
|
103
|
+
end
|
80
104
|
|
81
|
-
|
82
|
-
|
83
|
-
end
|
105
|
+
it "sets the identity for identities of 1 byte" do
|
106
|
+
sock = Socket.new @ctx.pointer, ZMQ::REQ
|
84
107
|
|
85
|
-
|
86
|
-
|
108
|
+
sock.identity = 'a'
|
109
|
+
sock.identity.should == 'a'
|
110
|
+
sock.close
|
111
|
+
end
|
87
112
|
|
88
|
-
|
89
|
-
|
90
|
-
end
|
113
|
+
it "set the identity identities of 255 bytes" do
|
114
|
+
sock = Socket.new @ctx.pointer, ZMQ::REQ
|
91
115
|
|
92
|
-
|
93
|
-
|
116
|
+
sock.identity = ('a' * 255)
|
117
|
+
sock.identity.should == ('a' * 255)
|
118
|
+
sock.close
|
119
|
+
end
|
94
120
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
121
|
+
it "should convert numeric identities to strings" do
|
122
|
+
sock = Socket.new @ctx.pointer, ZMQ::REQ
|
123
|
+
|
124
|
+
sock.identity = 7
|
125
|
+
sock.identity.should == '7'
|
126
|
+
sock.close
|
127
|
+
end
|
128
|
+
end # context identity=
|
100
129
|
|
130
|
+
end # version2? || version3?
|
101
131
|
|
102
|
-
|
132
|
+
|
133
|
+
socket_types.each do |socket_type|
|
103
134
|
|
104
135
|
context "#setsockopt for a #{ZMQ::SocketTypeNameMap[socket_type]} socket" do
|
105
136
|
before(:all) { @ctx = Context.new }
|
@@ -114,14 +145,116 @@ module ZMQ
|
|
114
145
|
end
|
115
146
|
|
116
147
|
|
148
|
+
if version2? || version3?
|
149
|
+
|
150
|
+
context "using option ZMQ::IDENTITY" do
|
151
|
+
it "should set the identity given any string under 255 characters" do
|
152
|
+
length = 4
|
153
|
+
(1..255).each do |length|
|
154
|
+
identity = 'a' * length
|
155
|
+
socket.setsockopt ZMQ::IDENTITY, identity
|
156
|
+
|
157
|
+
array = []
|
158
|
+
rc = socket.getsockopt(ZMQ::IDENTITY, array)
|
159
|
+
rc.should == 0
|
160
|
+
array[0].should == identity
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it "returns -1 given a string 256 characters or longer" do
|
165
|
+
identity = 'a' * 256
|
166
|
+
array = []
|
167
|
+
rc = socket.setsockopt(ZMQ::IDENTITY, identity)
|
168
|
+
rc.should == -1
|
169
|
+
end
|
170
|
+
end # context using option ZMQ::IDENTITY
|
171
|
+
|
172
|
+
end # version2? || version3?
|
173
|
+
|
174
|
+
|
175
|
+
if version2?
|
176
|
+
|
177
|
+
context "using option ZMQ::HWM" do
|
178
|
+
it "should set the high water mark given a positive value" do
|
179
|
+
hwm = 4
|
180
|
+
socket.setsockopt ZMQ::HWM, hwm
|
181
|
+
array = []
|
182
|
+
rc = socket.getsockopt(ZMQ::HWM, array)
|
183
|
+
rc.should == 0
|
184
|
+
array[0].should == hwm
|
185
|
+
end
|
186
|
+
end # context using option ZMQ::HWM
|
187
|
+
|
188
|
+
|
189
|
+
context "using option ZMQ::SWAP" do
|
190
|
+
it "should set the swap value given a positive value" do
|
191
|
+
swap = 10_000
|
192
|
+
socket.setsockopt ZMQ::SWAP, swap
|
193
|
+
array = []
|
194
|
+
rc = socket.getsockopt(ZMQ::SWAP, array)
|
195
|
+
rc.should == 0
|
196
|
+
array[0].should == swap
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns -1 given a negative value" do
|
200
|
+
swap = -10_000
|
201
|
+
rc = socket.setsockopt(ZMQ::SWAP, swap)
|
202
|
+
rc.should == -1
|
203
|
+
end
|
204
|
+
end # context using option ZMQ::SWP
|
205
|
+
|
206
|
+
|
207
|
+
context "using option ZMQ::MCAST_LOOP" do
|
208
|
+
it "should enable the multicast loopback given a 1 (true) value" do
|
209
|
+
socket.setsockopt ZMQ::MCAST_LOOP, 1
|
210
|
+
array = []
|
211
|
+
rc = socket.getsockopt(ZMQ::MCAST_LOOP, array)
|
212
|
+
rc.should == 0
|
213
|
+
array[0].should be_true
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should disable the multicast loopback given a 0 (false) value" do
|
217
|
+
socket.setsockopt ZMQ::MCAST_LOOP, 0
|
218
|
+
array = []
|
219
|
+
rc = socket.getsockopt(ZMQ::MCAST_LOOP, array)
|
220
|
+
rc.should == 0
|
221
|
+
array[0].should be_false
|
222
|
+
end
|
223
|
+
end # context using option ZMQ::MCAST_LOOP
|
224
|
+
|
225
|
+
|
226
|
+
context "using option ZMQ::RECOVERY_IVL_MSEC" do
|
227
|
+
it "should set the time interval for saving messages measured in milliseconds given a positive value" do
|
228
|
+
value = 200
|
229
|
+
socket.setsockopt ZMQ::RECOVERY_IVL_MSEC, value
|
230
|
+
array = []
|
231
|
+
rc = socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC, array)
|
232
|
+
rc.should == 0
|
233
|
+
array[0].should == value
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should default to a value of -1" do
|
237
|
+
value = -1
|
238
|
+
array = []
|
239
|
+
rc = socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC, array)
|
240
|
+
rc.should == 0
|
241
|
+
array[0].should == value
|
242
|
+
end
|
243
|
+
end # context using option ZMQ::RECOVERY_IVL_MSEC
|
244
|
+
|
245
|
+
end # version2?
|
246
|
+
|
247
|
+
|
117
248
|
context "using option ZMQ::SUBSCRIBE" do
|
118
249
|
if ZMQ::SUB == socket_type
|
119
|
-
it "
|
120
|
-
|
250
|
+
it "returns 0 for a SUB socket" do
|
251
|
+
rc = socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string")
|
252
|
+
rc.should == 0
|
121
253
|
end
|
122
254
|
else
|
123
|
-
it "
|
124
|
-
|
255
|
+
it "returns -1 for non-SUB sockets" do
|
256
|
+
rc = socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string")
|
257
|
+
rc.should == -1
|
125
258
|
end
|
126
259
|
end
|
127
260
|
end # context using option ZMQ::SUBSCRIBE
|
@@ -129,94 +262,47 @@ module ZMQ
|
|
129
262
|
|
130
263
|
context "using option ZMQ::UNSUBSCRIBE" do
|
131
264
|
if ZMQ::SUB == socket_type
|
132
|
-
it "
|
265
|
+
it "returns 0 given a topic string that was previously subscribed" do
|
133
266
|
socket.setsockopt ZMQ::SUBSCRIBE, "topic.string"
|
134
|
-
|
267
|
+
rc = socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string")
|
268
|
+
rc.should == 0
|
135
269
|
end
|
136
270
|
|
137
|
-
# it "should raise a ZMQ::SocketError given a topic string that was never subscribed" do
|
138
|
-
# socket.setsockopt ZMQ::SUBSCRIBE, "topic.string"
|
139
|
-
# lambda { socket.setsockopt(ZMQ::UNSUBSCRIBE, "unknown") }.should raise_error(SocketError)
|
140
|
-
# end
|
141
271
|
else
|
142
|
-
it "
|
143
|
-
|
272
|
+
it "returns -1 for non-SUB sockets" do
|
273
|
+
rc = socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string")
|
274
|
+
rc.should == -1
|
144
275
|
end
|
145
276
|
end
|
146
277
|
end # context using option ZMQ::UNSUBSCRIBE
|
147
278
|
|
148
279
|
|
149
|
-
context "using option ZMQ::HWM" do
|
150
|
-
it "should set the high water mark given a positive value" do
|
151
|
-
hwm = 4
|
152
|
-
socket.setsockopt ZMQ::HWM, hwm
|
153
|
-
socket.getsockopt(ZMQ::HWM).should == hwm
|
154
|
-
end
|
155
|
-
|
156
|
-
it "should convert a negative value to a positive value" do
|
157
|
-
hwm = -4
|
158
|
-
socket.setsockopt ZMQ::HWM, hwm
|
159
|
-
socket.getsockopt(ZMQ::HWM).should == hwm.abs
|
160
|
-
end
|
161
|
-
end # context using option ZMQ::HWM
|
162
|
-
|
163
|
-
|
164
|
-
context "using option ZMQ::SWAP" do
|
165
|
-
it "should set the swap value given a positive value" do
|
166
|
-
swap = 10_000
|
167
|
-
socket.setsockopt ZMQ::SWAP, swap
|
168
|
-
socket.getsockopt(ZMQ::SWAP).should == swap
|
169
|
-
end
|
170
|
-
|
171
|
-
it "should raise a SocketError given a negative value" do
|
172
|
-
swap = -10_000
|
173
|
-
lambda { socket.setsockopt(ZMQ::SWAP, swap) }.should raise_error(SocketError)
|
174
|
-
end
|
175
|
-
end # context using option ZMQ::SWP
|
176
|
-
|
177
|
-
|
178
280
|
context "using option ZMQ::AFFINITY" do
|
179
281
|
it "should set the affinity value given a positive value" do
|
180
282
|
affinity = 3
|
181
283
|
socket.setsockopt ZMQ::AFFINITY, affinity
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
affinity = -3
|
187
|
-
socket.setsockopt ZMQ::AFFINITY, affinity
|
188
|
-
socket.getsockopt(ZMQ::AFFINITY).should == affinity.abs
|
284
|
+
array = []
|
285
|
+
rc = socket.getsockopt(ZMQ::AFFINITY, array)
|
286
|
+
rc.should == 0
|
287
|
+
array[0].should == affinity
|
189
288
|
end
|
190
289
|
end # context using option ZMQ::AFFINITY
|
191
290
|
|
192
291
|
|
193
|
-
context "using option ZMQ::IDENTITY" do
|
194
|
-
it "should set the identity given any string under 255 characters" do
|
195
|
-
length = 4
|
196
|
-
(1..255).each do |length|
|
197
|
-
identity = 'a' * length
|
198
|
-
socket.setsockopt ZMQ::IDENTITY, identity
|
199
|
-
socket.getsockopt(ZMQ::IDENTITY).should == identity
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
it "should raise a SocketError given a string 256 characters or longer" do
|
204
|
-
identity = 'a' * 256
|
205
|
-
lambda { socket.setsockopt(ZMQ::IDENTITY, identity) }.should raise_error(SocketError)
|
206
|
-
end
|
207
|
-
end # context using option ZMQ::IDENTITY
|
208
|
-
|
209
|
-
|
210
292
|
context "using option ZMQ::RATE" do
|
211
293
|
it "should set the multicast send rate given a positive value" do
|
212
294
|
rate = 200
|
213
295
|
socket.setsockopt ZMQ::RATE, rate
|
214
|
-
|
296
|
+
array = []
|
297
|
+
rc = socket.getsockopt(ZMQ::RATE, array)
|
298
|
+
rc.should == 0
|
299
|
+
array[0].should == rate
|
215
300
|
end
|
216
301
|
|
217
|
-
it "
|
302
|
+
it "returns -1 given a negative value" do
|
218
303
|
rate = -200
|
219
|
-
|
304
|
+
rc = socket.setsockopt ZMQ::RATE, rate
|
305
|
+
rc.should == -1
|
220
306
|
end
|
221
307
|
end # context using option ZMQ::RATE
|
222
308
|
|
@@ -225,40 +311,28 @@ module ZMQ
|
|
225
311
|
it "should set the multicast recovery buffer measured in seconds given a positive value" do
|
226
312
|
rate = 200
|
227
313
|
socket.setsockopt ZMQ::RECOVERY_IVL, rate
|
228
|
-
|
314
|
+
array = []
|
315
|
+
rc = socket.getsockopt(ZMQ::RECOVERY_IVL, array)
|
316
|
+
rc.should == 0
|
317
|
+
array[0].should == rate
|
229
318
|
end
|
230
319
|
|
231
|
-
it "
|
320
|
+
it "returns -1 given a negative value" do
|
232
321
|
rate = -200
|
233
|
-
|
322
|
+
rc = socket.setsockopt ZMQ::RECOVERY_IVL, rate
|
323
|
+
rc.should == -1
|
234
324
|
end
|
235
325
|
end # context using option ZMQ::RECOVERY_IVL
|
236
326
|
|
237
327
|
|
238
|
-
context "using option ZMQ::MCAST_LOOP" do
|
239
|
-
it "should enable the multicast loopback given a true value" do
|
240
|
-
socket.setsockopt ZMQ::MCAST_LOOP, true
|
241
|
-
socket.getsockopt(ZMQ::MCAST_LOOP).should be_true
|
242
|
-
end
|
243
|
-
|
244
|
-
it "should disable the multicast loopback given a false value" do
|
245
|
-
socket.setsockopt ZMQ::MCAST_LOOP, false
|
246
|
-
socket.getsockopt(ZMQ::MCAST_LOOP).should be_false
|
247
|
-
end
|
248
|
-
end # context using option ZMQ::MCAST_LOOP
|
249
|
-
|
250
|
-
|
251
328
|
context "using option ZMQ::SNDBUF" do
|
252
329
|
it "should set the OS send buffer given a positive value" do
|
253
330
|
size = 100
|
254
331
|
socket.setsockopt ZMQ::SNDBUF, size
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
size = -100
|
260
|
-
socket.setsockopt ZMQ::SNDBUF, size
|
261
|
-
socket.getsockopt(ZMQ::SNDBUF).should == size.abs
|
332
|
+
array = []
|
333
|
+
rc = socket.getsockopt(ZMQ::SNDBUF, array)
|
334
|
+
rc.should == 0
|
335
|
+
array[0].should == size
|
262
336
|
end
|
263
337
|
end # context using option ZMQ::SNDBUF
|
264
338
|
|
@@ -267,13 +341,10 @@ module ZMQ
|
|
267
341
|
it "should set the OS receive buffer given a positive value" do
|
268
342
|
size = 100
|
269
343
|
socket.setsockopt ZMQ::RCVBUF, size
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
size = -100
|
275
|
-
socket.setsockopt ZMQ::RCVBUF, size
|
276
|
-
socket.getsockopt(ZMQ::RCVBUF).should == size.abs
|
344
|
+
array = []
|
345
|
+
rc = socket.getsockopt(ZMQ::RCVBUF, array)
|
346
|
+
rc.should == 0
|
347
|
+
array[0].should == size
|
277
348
|
end
|
278
349
|
end # context using option ZMQ::RCVBUF
|
279
350
|
|
@@ -282,18 +353,27 @@ module ZMQ
|
|
282
353
|
it "should set the socket message linger option measured in milliseconds given a positive value" do
|
283
354
|
value = 200
|
284
355
|
socket.setsockopt ZMQ::LINGER, value
|
285
|
-
|
356
|
+
array = []
|
357
|
+
rc = socket.getsockopt(ZMQ::LINGER, array)
|
358
|
+
rc.should == 0
|
359
|
+
array[0].should == value
|
286
360
|
end
|
287
361
|
|
288
362
|
it "should set the socket message linger option to 0 for dropping packets" do
|
289
363
|
value = 0
|
290
364
|
socket.setsockopt ZMQ::LINGER, value
|
291
|
-
|
365
|
+
array = []
|
366
|
+
rc = socket.getsockopt(ZMQ::LINGER, array)
|
367
|
+
rc.should == 0
|
368
|
+
array[0].should == value
|
292
369
|
end
|
293
370
|
|
294
371
|
it "should default to a value of -1" do
|
295
372
|
value = -1
|
296
|
-
|
373
|
+
array = []
|
374
|
+
rc = socket.getsockopt(ZMQ::LINGER, array)
|
375
|
+
rc.should == 0
|
376
|
+
array[0].should == value
|
297
377
|
end
|
298
378
|
end # context using option ZMQ::LINGER
|
299
379
|
|
@@ -302,12 +382,18 @@ module ZMQ
|
|
302
382
|
it "should set the time interval for reconnecting disconnected sockets measured in milliseconds given a positive value" do
|
303
383
|
value = 200
|
304
384
|
socket.setsockopt ZMQ::RECONNECT_IVL, value
|
305
|
-
|
385
|
+
array = []
|
386
|
+
rc = socket.getsockopt(ZMQ::RECONNECT_IVL, array)
|
387
|
+
rc.should == 0
|
388
|
+
array[0].should == value
|
306
389
|
end
|
307
390
|
|
308
391
|
it "should default to a value of 100" do
|
309
392
|
value = 100
|
310
|
-
|
393
|
+
array = []
|
394
|
+
rc = socket.getsockopt(ZMQ::RECONNECT_IVL, array)
|
395
|
+
rc.should == 0
|
396
|
+
array[0].should == value
|
311
397
|
end
|
312
398
|
end # context using option ZMQ::RECONNECT_IVL
|
313
399
|
|
@@ -316,28 +402,20 @@ module ZMQ
|
|
316
402
|
it "should set the maximum number of pending socket connections given a positive value" do
|
317
403
|
value = 200
|
318
404
|
socket.setsockopt ZMQ::BACKLOG, value
|
319
|
-
|
405
|
+
array = []
|
406
|
+
rc = socket.getsockopt(ZMQ::BACKLOG, array)
|
407
|
+
rc.should == 0
|
408
|
+
array[0].should == value
|
320
409
|
end
|
321
410
|
|
322
411
|
it "should default to a value of 100" do
|
323
412
|
value = 100
|
324
|
-
|
413
|
+
array = []
|
414
|
+
rc = socket.getsockopt(ZMQ::BACKLOG, array)
|
415
|
+
rc.should == 0
|
416
|
+
array[0].should == value
|
325
417
|
end
|
326
418
|
end # context using option ZMQ::BACKLOG
|
327
|
-
|
328
|
-
|
329
|
-
context "using option ZMQ::RECOVERY_IVL_MSEC" do
|
330
|
-
it "should set the time interval for saving messages measured in milliseconds given a positive value" do
|
331
|
-
value = 200
|
332
|
-
socket.setsockopt ZMQ::RECOVERY_IVL_MSEC, value
|
333
|
-
socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC).should == value
|
334
|
-
end
|
335
|
-
|
336
|
-
it "should default to a value of -1" do
|
337
|
-
value = -1
|
338
|
-
socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC).should == value
|
339
|
-
end
|
340
|
-
end # context using option ZMQ::RECOVERY_IVL_MSEC
|
341
419
|
end # context #setsockopt
|
342
420
|
|
343
421
|
|
@@ -353,48 +431,70 @@ module ZMQ
|
|
353
431
|
socket.close
|
354
432
|
end
|
355
433
|
|
356
|
-
|
357
|
-
|
358
|
-
socket.getsockopt(ZMQ::FD).should be_a(Fixnum)
|
359
|
-
end
|
434
|
+
if RUBY_PLATFORM =~ /linux|darwin/
|
435
|
+
# this spec doesn't work on Windows; hints welcome
|
360
436
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
# figures out the correct libc for each platform including Windows
|
368
|
-
library = ffi_lib(FFI::Library::LIBC).first
|
369
|
-
attach_function :getsockopt, [:int, :int, :int, :pointer, :pointer], :int
|
370
|
-
end # module LibC
|
371
|
-
|
372
|
-
if RUBY_PLATFORM =~ /linux/ || (RUBY_PLATFORM == 'java' && `uname` =~ /linux/i)
|
373
|
-
so_rcvbuf = 8
|
374
|
-
sol_socket = 1
|
375
|
-
else #OSX
|
376
|
-
so_rcvbuf = 0x1002
|
377
|
-
sol_socket = 0xffff
|
437
|
+
context "using option ZMQ::FD" do
|
438
|
+
it "should return an FD as a positive integer" do
|
439
|
+
array = []
|
440
|
+
rc = socket.getsockopt(ZMQ::FD, array)
|
441
|
+
rc.should == 0
|
442
|
+
array[0].should be_a(Fixnum)
|
378
443
|
end
|
379
444
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
445
|
+
it "returns a valid FD that is accepted by the system poll() function" do
|
446
|
+
# Use FFI to wrap the C library function +poll+ so that we can execute it
|
447
|
+
# on the 0mq file descriptor. If it returns 0, then it succeeded and the FD
|
448
|
+
# is valid!
|
449
|
+
module LibSocket
|
450
|
+
extend FFI::Library
|
451
|
+
# figures out the correct libc for each platform including Windows
|
452
|
+
library = ffi_lib(FFI::Library::LIBC).first
|
453
|
+
|
454
|
+
find_type(:nfds_t) rescue typedef(:uint32, :nfds_t)
|
455
|
+
|
456
|
+
attach_function :poll, [:pointer, :nfds_t, :int], :int
|
457
|
+
|
458
|
+
class PollFD < FFI::Struct
|
459
|
+
layout :fd, :int,
|
460
|
+
:events, :short,
|
461
|
+
:revents, :short
|
462
|
+
end
|
463
|
+
end # module LibSocket
|
464
|
+
|
465
|
+
array = []
|
466
|
+
rc = socket.getsockopt(ZMQ::FD, array)
|
467
|
+
rc.should be_zero
|
468
|
+
fd = array[0]
|
469
|
+
|
470
|
+
# setup the BSD poll_fd struct
|
471
|
+
pollfd = LibSocket::PollFD.new
|
472
|
+
pollfd[:fd] = fd
|
473
|
+
pollfd[:events] = 0
|
474
|
+
pollfd[:revents] = 0
|
475
|
+
|
476
|
+
rc = LibSocket.poll(pollfd, 1, 0)
|
477
|
+
rc.should be_zero
|
478
|
+
end
|
386
479
|
end
|
387
|
-
|
480
|
+
|
481
|
+
end # posix platform
|
388
482
|
|
389
483
|
context "using option ZMQ::EVENTS" do
|
390
484
|
it "should return a mask of events as a Fixnum" do
|
391
|
-
|
485
|
+
array = []
|
486
|
+
rc = socket.getsockopt(ZMQ::EVENTS, array)
|
487
|
+
rc.should == 0
|
488
|
+
array[0].should be_a(Fixnum)
|
392
489
|
end
|
393
490
|
end
|
394
491
|
|
395
492
|
context "using option ZMQ::TYPE" do
|
396
493
|
it "should return the socket type" do
|
397
|
-
|
494
|
+
array = []
|
495
|
+
rc = socket.getsockopt(ZMQ::TYPE, array)
|
496
|
+
rc.should == 0
|
497
|
+
array[0].should == socket_type
|
398
498
|
end
|
399
499
|
end
|
400
500
|
end # context #getsockopt
|
@@ -402,38 +502,87 @@ module ZMQ
|
|
402
502
|
end # each socket_type
|
403
503
|
|
404
504
|
|
405
|
-
describe "
|
505
|
+
describe "Mapping socket EVENTS to POLLIN and POLLOUT" do
|
406
506
|
include APIHelper
|
507
|
+
|
508
|
+
shared_examples_for "pubsub sockets where" do
|
509
|
+
it "SUB socket that received a message always has POLLIN set" do
|
510
|
+
events = []
|
511
|
+
rc = @sub.getsockopt(ZMQ::EVENTS, events)
|
512
|
+
rc.should == 0
|
513
|
+
events[0].should == ZMQ::POLLIN
|
514
|
+
end
|
515
|
+
|
516
|
+
it "PUB socket always has POLLOUT set" do
|
517
|
+
events = []
|
518
|
+
rc = @pub.getsockopt(ZMQ::EVENTS, events)
|
519
|
+
rc.should == 0
|
520
|
+
events[0].should == ZMQ::POLLOUT
|
521
|
+
end
|
407
522
|
|
408
|
-
|
409
|
-
|
410
|
-
|
523
|
+
it "PUB socket never has POLLIN set" do
|
524
|
+
events = []
|
525
|
+
rc = @pub.getsockopt(ZMQ::EVENTS, events)
|
526
|
+
rc.should == 0
|
527
|
+
events[0].should_not == ZMQ::POLLIN
|
528
|
+
end
|
411
529
|
|
412
|
-
|
413
|
-
|
530
|
+
it "SUB socket never has POLLOUT set" do
|
531
|
+
events = []
|
532
|
+
rc = @sub.getsockopt(ZMQ::EVENTS, events)
|
533
|
+
rc.should == 0
|
534
|
+
events[0].should_not == ZMQ::POLLOUT
|
535
|
+
end
|
536
|
+
end # shared example for pubsub
|
414
537
|
|
415
|
-
|
416
|
-
@pub.connect addr
|
538
|
+
context "when SUB binds and PUB connects" do
|
417
539
|
|
418
|
-
|
540
|
+
before(:each) do
|
541
|
+
@ctx = Context.new
|
419
542
|
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
543
|
+
@sub = @ctx.socket ZMQ::SUB
|
544
|
+
rc = @sub.setsockopt ZMQ::SUBSCRIBE, ''
|
545
|
+
|
546
|
+
@pub = @ctx.socket ZMQ::PUB
|
547
|
+
port = bind_to_random_tcp_port(@sub)
|
548
|
+
rc = @pub.connect "tcp://127.0.0.1:#{port}"
|
549
|
+
sleep 0.5
|
550
|
+
|
551
|
+
rc = @pub.send_string('test')
|
552
|
+
sleep 0.2
|
553
|
+
end
|
554
|
+
|
555
|
+
it_behaves_like "pubsub sockets where"
|
556
|
+
end # context SUB binds PUB connects
|
557
|
+
|
558
|
+
context "when SUB connects and PUB binds" do
|
559
|
+
|
560
|
+
before(:each) do
|
561
|
+
@ctx = Context.new
|
562
|
+
|
563
|
+
@sub = @ctx.socket ZMQ::SUB
|
564
|
+
rc = @sub.setsockopt ZMQ::SUBSCRIBE, ''
|
565
|
+
|
566
|
+
@pub = @ctx.socket ZMQ::PUB
|
567
|
+
port = bind_to_random_tcp_port(@pub)
|
568
|
+
rc = @sub.connect "tcp://127.0.0.1:#{port}"
|
569
|
+
sleep 0.5
|
570
|
+
|
571
|
+
rc = @pub.send_string('test')
|
572
|
+
sleep 0.2
|
573
|
+
end
|
574
|
+
|
575
|
+
it_behaves_like "pubsub sockets where"
|
576
|
+
end # context SUB binds PUB connects
|
577
|
+
|
578
|
+
|
579
|
+
after(:each) do
|
424
580
|
@sub.close
|
425
581
|
@pub.close
|
426
582
|
# must call close on *every* socket before calling terminate otherwise it blocks indefinitely
|
427
583
|
@ctx.terminate
|
428
584
|
end
|
429
585
|
|
430
|
-
it "should have only POLLIN set for a sub socket that received a message" do
|
431
|
-
#@sub.getsockopt(ZMQ::EVENTS).should == ZMQ::POLLIN
|
432
|
-
end
|
433
|
-
|
434
|
-
it "should have only POLLOUT set for a sub socket that received a message" do
|
435
|
-
#@pub.getsockopt(ZMQ::EVENTS).should == ZMQ::POLLOUT
|
436
|
-
end
|
437
586
|
end # describe 'events mapping to pollin and pollout'
|
438
587
|
|
439
588
|
end # describe Socket
|