ffi-rzmq 0.9.3 → 0.9.6
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 +51 -0
- data/README.rdoc +17 -7
- data/examples/v2api/local_lat_poll.rb +3 -3
- data/examples/v2api/local_throughput.rb +2 -2
- data/examples/v2api/pub.rb +46 -0
- data/examples/v2api/publish_subscribe.rb +1 -1
- data/examples/v2api/remote_throughput.rb +1 -1
- data/examples/v2api/reqrep_poll.rb +2 -2
- data/examples/v2api/request_response.rb +2 -2
- data/examples/v2api/sub.rb +74 -0
- data/examples/v2api/throughput_measurement.rb +3 -3
- data/examples/v2api/xreqxrep_poll.rb +5 -5
- data/examples/v3api/local_lat_poll.rb +3 -3
- data/examples/v3api/pub.rb +46 -0
- data/examples/v3api/publish_subscribe.rb +1 -1
- data/examples/v3api/reqrep_poll.rb +2 -2
- data/examples/v3api/sub.rb +69 -0
- data/examples/v3api/xreqxrep_poll.rb +5 -5
- data/ffi-rzmq.gemspec +2 -2
- data/lib/ffi-rzmq/constants.rb +56 -18
- data/lib/ffi-rzmq/context.rb +63 -24
- data/lib/ffi-rzmq/device.rb +19 -19
- data/lib/ffi-rzmq/libzmq.rb +109 -34
- data/lib/ffi-rzmq/message.rb +23 -2
- data/lib/ffi-rzmq/poll.rb +7 -9
- data/lib/ffi-rzmq/socket.rb +284 -518
- data/lib/ffi-rzmq/util.rb +42 -36
- data/lib/ffi-rzmq/version.rb +1 -1
- data/spec/context_spec.rb +20 -16
- data/spec/device_spec.rb +64 -76
- data/spec/multipart_spec.rb +0 -54
- data/spec/nonblocking_recv_spec.rb +119 -80
- data/spec/poll_spec.rb +93 -12
- data/spec/pushpull_spec.rb +65 -111
- data/spec/reqrep_spec.rb +45 -56
- data/spec/socket_spec.rb +104 -71
- data/spec/spec_helper.rb +52 -4
- metadata +155 -135
data/spec/poll_spec.rb
CHANGED
@@ -51,31 +51,112 @@ module ZMQ
|
|
51
51
|
|
52
52
|
|
53
53
|
context "#delete" do
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
before(:all) do
|
55
|
+
@context = Context.new
|
56
|
+
end
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@socket = @context.socket(XREQ)
|
60
|
+
@socket.setsockopt(LINGER, 0)
|
61
|
+
@poller = Poller.new
|
62
|
+
end
|
63
|
+
|
64
|
+
after(:each) do
|
65
|
+
@socket.close
|
66
|
+
end
|
67
|
+
|
68
|
+
after(:all) do
|
69
|
+
@context.terminate
|
70
|
+
end
|
57
71
|
|
58
72
|
it "should return false for an unregistered socket (i.e. not found)" do
|
59
|
-
poller.delete(socket).should be_false
|
73
|
+
@poller.delete(@socket).should be_false
|
60
74
|
end
|
61
75
|
|
62
76
|
it "returns true for a sucessfully deleted socket when only 1 is registered" do
|
63
|
-
socket1 = context.socket
|
77
|
+
socket1 = @context.socket(REP)
|
78
|
+
socket1.setsockopt(LINGER, 0)
|
64
79
|
|
65
|
-
poller.register socket1
|
66
|
-
poller.delete(socket1).should be_true
|
80
|
+
@poller.register socket1
|
81
|
+
@poller.delete(socket1).should be_true
|
82
|
+
socket1.close
|
67
83
|
end
|
68
84
|
|
69
85
|
it "returns true for a sucessfully deleted socket when more than 1 is registered" do
|
70
|
-
socket1 = context.socket
|
71
|
-
socket2 = context.socket
|
86
|
+
socket1 = @context.socket(REP)
|
87
|
+
socket2 = @context.socket(REP)
|
88
|
+
socket1.setsockopt(LINGER, 0)
|
89
|
+
socket2.setsockopt(LINGER, 0)
|
72
90
|
|
73
|
-
poller.register socket1
|
74
|
-
poller.register socket2
|
75
|
-
poller.delete(socket2).should be_true
|
91
|
+
@poller.register socket1
|
92
|
+
@poller.register socket2
|
93
|
+
@poller.delete(socket2).should be_true
|
94
|
+
socket1.close
|
95
|
+
socket2.close
|
76
96
|
end
|
77
97
|
|
78
98
|
end
|
99
|
+
|
100
|
+
|
101
|
+
context "poll" do
|
102
|
+
include APIHelper
|
103
|
+
|
104
|
+
before(:all) do
|
105
|
+
end
|
106
|
+
|
107
|
+
before(:each) do
|
108
|
+
# Must recreate context for each test otherwise some poll tests fail.
|
109
|
+
# This is likely due to a race condition in event handling when reusing
|
110
|
+
# the same inproc inside the same context over and over. Making a new
|
111
|
+
# context solves it.
|
112
|
+
@context = Context.new
|
113
|
+
endpoint = "inproc://poll_test"
|
114
|
+
@socket = @context.socket(DEALER)
|
115
|
+
@socket2 = @context.socket(ROUTER)
|
116
|
+
@socket.setsockopt(LINGER, 0)
|
117
|
+
@socket2.setsockopt(LINGER, 0)
|
118
|
+
@socket.bind(endpoint)
|
119
|
+
connect_to_inproc(@socket2, endpoint)
|
120
|
+
|
121
|
+
@poller = Poller.new
|
122
|
+
end
|
123
|
+
|
124
|
+
after(:each) do
|
125
|
+
@socket.close
|
126
|
+
@socket2.close
|
127
|
+
@context.terminate
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns 0 when there are no sockets to poll" do
|
131
|
+
rc = @poller.poll(100)
|
132
|
+
rc.should be_zero
|
133
|
+
end
|
134
|
+
|
135
|
+
it "returns 0 when there is a single socket to poll and no events" do
|
136
|
+
@poller.register(@socket, 0)
|
137
|
+
rc = @poller.poll(100)
|
138
|
+
rc.should be_zero
|
139
|
+
end
|
140
|
+
|
141
|
+
it "returns 1 when there is a read event on a socket" do
|
142
|
+
@poller.register_readable(@socket2)
|
143
|
+
|
144
|
+
@socket.send_string('test')
|
145
|
+
rc = @poller.poll(1000)
|
146
|
+
rc.should == 1
|
147
|
+
end
|
148
|
+
|
149
|
+
it "returns 1 when there is a read event on one socket and the second socket has been removed from polling" do
|
150
|
+
@poller.register_readable(@socket2)
|
151
|
+
@poller.register_writable(@socket)
|
152
|
+
|
153
|
+
@socket.send_string('test')
|
154
|
+
@poller.deregister_writable(@socket)
|
155
|
+
|
156
|
+
rc = @poller.poll(1000)
|
157
|
+
rc.should == 1
|
158
|
+
end
|
159
|
+
end # poll
|
79
160
|
|
80
161
|
|
81
162
|
end # describe Poll
|
data/spec/pushpull_spec.rb
CHANGED
@@ -2,23 +2,26 @@
|
|
2
2
|
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
3
3
|
|
4
4
|
module ZMQ
|
5
|
-
describe
|
5
|
+
describe Socket do
|
6
6
|
context "when running basic push pull" do
|
7
7
|
include APIHelper
|
8
8
|
|
9
9
|
let(:string) { "booga-booga" }
|
10
|
-
|
10
|
+
|
11
11
|
before(:each) do
|
12
|
-
|
13
|
-
|
12
|
+
# Use new context for each iteration to avoid inproc race. See
|
13
|
+
# poll_spec.rb for more details.
|
14
|
+
@context = Context.new
|
15
|
+
poller_setup
|
16
|
+
|
14
17
|
@push = @context.socket ZMQ::PUSH
|
15
18
|
@pull = @context.socket ZMQ::PULL
|
16
19
|
@push.setsockopt ZMQ::LINGER, 0
|
17
20
|
@pull.setsockopt ZMQ::LINGER, 0
|
18
|
-
|
19
|
-
@link = "
|
20
|
-
|
21
|
-
@
|
21
|
+
|
22
|
+
@link = "inproc://push_pull_test"
|
23
|
+
@push.bind @link
|
24
|
+
connect_to_inproc(@pull, @link)
|
22
25
|
end
|
23
26
|
|
24
27
|
after(:each) do
|
@@ -35,126 +38,77 @@ module ZMQ
|
|
35
38
|
received.should == string
|
36
39
|
end
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
received_message = Message.new
|
42
|
-
|
43
|
-
rc = @push.send sent_message
|
44
|
-
rc.should == 0
|
45
|
-
sleep 0.1 # give it time for delivery
|
46
|
-
rc = @pull.recv received_message, ZMQ::NOBLOCK
|
47
|
-
received_message.copy_out_string.should == string
|
48
|
-
end
|
49
|
-
|
50
|
-
else
|
51
|
-
|
52
|
-
it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
|
53
|
-
sent_message = Message.new string
|
54
|
-
received_message = Message.new
|
41
|
+
it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
|
42
|
+
sent_message = Message.new string
|
43
|
+
received_message = Message.new
|
55
44
|
|
45
|
+
poll_it_for_read(@pull) do
|
56
46
|
rc = @push.sendmsg sent_message
|
57
|
-
rc.should == string.size
|
58
|
-
sleep 0.1 # give it time for delivery
|
59
|
-
rc = @pull.recvmsg received_message, ZMQ::DONTWAIT
|
60
|
-
rc.should == string.size
|
61
|
-
received_message.copy_out_string.should == string
|
47
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
62
48
|
end
|
49
|
+
|
50
|
+
rc = @pull.recvmsg received_message, ZMQ::NonBlocking
|
51
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
52
|
+
received_message.copy_out_string.should == string
|
63
53
|
end
|
64
54
|
|
65
55
|
|
66
|
-
if version2?
|
67
|
-
|
68
|
-
it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages and a unique socket per thread" do
|
69
|
-
received = []
|
70
|
-
threads = []
|
71
|
-
count = 4
|
72
|
-
@pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
|
73
|
-
mutex = Mutex.new
|
74
|
-
|
75
|
-
count.times do |i|
|
76
|
-
threads << Thread.new do
|
77
|
-
pull = @context.socket ZMQ::PULL
|
78
|
-
rc = pull.setsockopt ZMQ::LINGER, 0
|
79
|
-
rc = pull.connect @link
|
80
|
-
rc.should == 0
|
81
|
-
buffer = ''
|
82
|
-
rc = pull.recv_string buffer
|
83
|
-
rc.should == 0
|
84
|
-
mutex.synchronize { received << buffer }
|
85
|
-
pull.close
|
86
|
-
end
|
87
|
-
sleep 0.01 # give each thread time to spin up
|
88
|
-
end
|
89
|
-
|
90
|
-
count.times { @push.send_string(string) }
|
91
|
-
|
92
|
-
threads.each {|t| t.join}
|
93
56
|
|
94
|
-
|
57
|
+
it "should receive a single message for each message sent on each socket listening, when an equal number of sockets pulls messages and where each socket is unique per thread" do
|
58
|
+
received = []
|
59
|
+
threads = []
|
60
|
+
sockets = []
|
61
|
+
count = 4
|
62
|
+
mutex = Mutex.new
|
63
|
+
|
64
|
+
# make sure all sockets are connected before we do our load-balancing test
|
65
|
+
(count - 1).times do
|
66
|
+
socket = @context.socket ZMQ::PULL
|
67
|
+
socket.setsockopt ZMQ::LINGER, 0
|
68
|
+
connect_to_inproc(socket, @link)
|
69
|
+
sockets << socket
|
95
70
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
threads
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
rc.should == 0
|
106
|
-
mutex = Mutex.new
|
107
|
-
|
108
|
-
count.times do |i|
|
109
|
-
threads << Thread.new do
|
110
|
-
buffer = ''
|
111
|
-
rc = 0
|
112
|
-
mutex.synchronize { rc = pull.recv_string buffer }
|
113
|
-
rc.should == 0
|
114
|
-
mutex.synchronize { received << buffer }
|
115
|
-
end
|
116
|
-
sleep 0.01 # give each thread time to spin up
|
71
|
+
sockets << @pull
|
72
|
+
|
73
|
+
sockets.each do |socket|
|
74
|
+
threads << Thread.new do
|
75
|
+
buffer = ''
|
76
|
+
rc = socket.recv_string buffer
|
77
|
+
version2? ? (rc.should == 0) : (rc.should == buffer.size)
|
78
|
+
mutex.synchronize { received << buffer }
|
79
|
+
socket.close
|
117
80
|
end
|
81
|
+
end
|
118
82
|
|
119
|
-
|
83
|
+
count.times { @push.send_string(string) }
|
120
84
|
|
121
|
-
|
122
|
-
pull.close
|
85
|
+
threads.each {|t| t.join}
|
123
86
|
|
124
|
-
|
125
|
-
|
87
|
+
received.find_all {|r| r == string}.length.should == count
|
88
|
+
end
|
126
89
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
rc.should == 0
|
141
|
-
buffer = ''
|
142
|
-
rc = pull.recv_string buffer
|
143
|
-
rc.should == string.size
|
144
|
-
received << buffer
|
145
|
-
pull.close
|
146
|
-
end
|
147
|
-
sleep 0.01 # give each thread time to spin up
|
90
|
+
it "should receive a single message for each message sent when using a single shared socket protected by a mutex" do
|
91
|
+
received = []
|
92
|
+
threads = []
|
93
|
+
count = 4
|
94
|
+
mutex = Mutex.new
|
95
|
+
|
96
|
+
count.times do |i|
|
97
|
+
threads << Thread.new do
|
98
|
+
buffer = ''
|
99
|
+
rc = 0
|
100
|
+
mutex.synchronize { rc = @pull.recv_string buffer }
|
101
|
+
version2? ? (rc.should == 0) : (rc.should == buffer.size)
|
102
|
+
mutex.synchronize { received << buffer }
|
148
103
|
end
|
104
|
+
end
|
149
105
|
|
150
|
-
|
151
|
-
|
152
|
-
threads.each {|t| t.join}
|
106
|
+
count.times { @push.send_string(string) }
|
153
107
|
|
154
|
-
|
155
|
-
end
|
108
|
+
threads.each {|t| t.join}
|
156
109
|
|
157
|
-
|
110
|
+
received.find_all {|r| r == string}.length.should == count
|
111
|
+
end
|
158
112
|
|
159
113
|
end # @context ping-pong
|
160
114
|
end # describe
|
data/spec/reqrep_spec.rb
CHANGED
@@ -4,88 +4,77 @@ require File.join(File.dirname(__FILE__), %w[spec_helper])
|
|
4
4
|
module ZMQ
|
5
5
|
|
6
6
|
|
7
|
-
describe
|
7
|
+
describe Socket do
|
8
8
|
|
9
9
|
context "when running ping pong" do
|
10
10
|
include APIHelper
|
11
11
|
|
12
12
|
let(:string) { "booga-booga" }
|
13
13
|
|
14
|
+
# reset sockets each time because we only send 1 message which leaves
|
15
|
+
# the REQ socket in a bad state. It cannot send again unless we were to
|
16
|
+
# send a reply with the REP and read it.
|
14
17
|
before(:each) do
|
15
|
-
context = ZMQ::Context.new
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@ping.
|
18
|
+
@context = ZMQ::Context.new
|
19
|
+
poller_setup
|
20
|
+
|
21
|
+
endpoint = "inproc://reqrep_test"
|
22
|
+
@ping = @context.socket ZMQ::REQ
|
23
|
+
@pong = @context.socket ZMQ::REP
|
24
|
+
@pong.bind(endpoint)
|
25
|
+
connect_to_inproc(@ping, endpoint)
|
20
26
|
end
|
21
27
|
|
22
28
|
after(:each) do
|
23
29
|
@ping.close
|
24
30
|
@pong.close
|
31
|
+
@context.terminate
|
25
32
|
end
|
26
|
-
|
27
|
-
|
33
|
+
|
34
|
+
def send_ping(string)
|
28
35
|
@ping.send_string string
|
29
36
|
received_message = ''
|
30
37
|
rc = @pong.recv_string received_message
|
38
|
+
[rc, received_message]
|
39
|
+
end
|
31
40
|
|
41
|
+
it "should receive an exact string copy of the string message sent" do
|
42
|
+
rc, received_message = send_ping(string)
|
32
43
|
received_message.should == string
|
33
44
|
end
|
45
|
+
|
46
|
+
it "should generate a EFSM error when sending via the REQ socket twice in a row without an intervening receive operation" do
|
47
|
+
send_ping(string)
|
48
|
+
rc = @ping.send_string(string)
|
49
|
+
rc.should == -1
|
50
|
+
Util.errno.should == ZMQ::EFSM
|
51
|
+
end
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
sent_message = Message.new string
|
38
|
-
received_message = Message.new
|
39
|
-
|
40
|
-
rc = @ping.send sent_message
|
41
|
-
rc.should == 0
|
42
|
-
rc = @pong.recv received_message
|
43
|
-
rc.should == 0
|
44
|
-
|
45
|
-
received_message.copy_out_string.should == string
|
46
|
-
end
|
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
|
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
|
53
|
+
it "should receive an exact copy of the sent message using Message objects directly" do
|
54
|
+
received_message = Message.new
|
71
55
|
|
72
|
-
|
73
|
-
|
56
|
+
rc = @ping.sendmsg(Message.new(string))
|
57
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
58
|
+
rc = @pong.recvmsg received_message
|
59
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
74
60
|
|
75
|
-
|
76
|
-
|
77
|
-
received_message = Message.new
|
61
|
+
received_message.copy_out_string.should == string
|
62
|
+
end
|
78
63
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
rc = @pong.recvmsg received_message, ZMQ::DONTWAIT
|
83
|
-
rc.should == string.size
|
64
|
+
it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
|
65
|
+
sent_message = Message.new string
|
66
|
+
received_message = Message.new
|
84
67
|
|
85
|
-
|
68
|
+
poll_it_for_read(@pong) do
|
69
|
+
rc = @ping.sendmsg(Message.new(string), ZMQ::NonBlocking)
|
70
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
86
71
|
end
|
72
|
+
|
73
|
+
rc = @pong.recvmsg received_message, ZMQ::NonBlocking
|
74
|
+
LibZMQ.version2? ? rc.should == 0 : rc.should == string.size
|
87
75
|
|
88
|
-
|
76
|
+
received_message.copy_out_string.should == string
|
77
|
+
end
|
89
78
|
|
90
79
|
end # context ping-pong
|
91
80
|
|