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/History.txt
CHANGED
@@ -1,3 +1,52 @@
|
|
1
|
+
== 0.9.6 / 20120808
|
2
|
+
* Never released 0.9.5 as a gem. It was available via github only.
|
3
|
+
|
4
|
+
* Improved error message when DLL loading fails on Windows.
|
5
|
+
|
6
|
+
* Added support for 0mq 2.2. Support for 2.1 might be getting shakey...
|
7
|
+
patches to make it fully support 2.1 (assuming it's even broken at
|
8
|
+
all) are welcome.
|
9
|
+
|
10
|
+
* Added support for 0mq 3.2 (no support for 3.0 or 3.1). Not all methods
|
11
|
+
are exposed yet. For example, setting values on the context after it
|
12
|
+
has been created is not supported; instead, pass the correct keys
|
13
|
+
(:io_threads and :max_sockets) to the call to Context.create or
|
14
|
+
Context#new.
|
15
|
+
|
16
|
+
* Reduced spec running time from 30+ seconds to under 1 by eliminating
|
17
|
+
most uses of "sleep." It now polls sockets to wait for message
|
18
|
+
delivery. It also uses a technique of binding an inproc transport and
|
19
|
+
busy-looping on the connect side until it succeeds. These techniques
|
20
|
+
both allowed me to eliminate most uses of sleep.
|
21
|
+
|
22
|
+
* Some changes to support usage on Win7x64 with a 64-bit libzmq DLL.
|
23
|
+
|
24
|
+
== 0.9.5 / 20120119
|
25
|
+
* BROKE THE API.
|
26
|
+
In 0mq 2.x, there were two functions zmq_send() and zmq_recv().
|
27
|
+
As of 3.x, those functions were renamed zmq_sendmsg() and
|
28
|
+
zmq_recvmsg(). As everyone starts moving to 0mq 3.x, it doesn't
|
29
|
+
make sense to make the code break with 2.x. So, I'm breaking this
|
30
|
+
binding so that it always uses sendmsg/recvmsg and eliminates the
|
31
|
+
original send/recv methods.
|
32
|
+
Sorry!
|
33
|
+
This is likely to be the last non-backward-compatible API breakage.
|
34
|
+
Release 1.0 is around the corner and the API will be stable (I follow
|
35
|
+
semantic versioning).
|
36
|
+
|
37
|
+
* Introduced ZMQ::NonBlocking. This flag returns the correct value to set
|
38
|
+
a socket in non-blocking mode when sending/receiving. This hides the
|
39
|
+
differences between 0mq 2.x and 3.x since the constant names have
|
40
|
+
changed.
|
41
|
+
|
42
|
+
== 0.9.4 / 20120102
|
43
|
+
* Fixed bug in Poller#delete. Added specs to catch a regression.
|
44
|
+
In short, a socket that was deleted from the Poller set wasn't
|
45
|
+
always actually *removed* from the array. This led to a closed
|
46
|
+
socket being part of the pollset which would return errno 38.
|
47
|
+
This took about 4 days to find. <sigh>
|
48
|
+
|
49
|
+
|
1
50
|
== 0.9.3 / 20111214
|
2
51
|
* Performance optimizations for #getsockopt.
|
3
52
|
|
@@ -15,6 +64,8 @@
|
|
15
64
|
can avoid that work and dispatch directly. This effects all
|
16
65
|
Ruby runtimes, but it was through the work of Evan Phoenix that
|
17
66
|
I figured this out. Results in a 2-5% speedup on method dispatch.
|
67
|
+
|
68
|
+
|
18
69
|
|
19
70
|
== 0.9.2 / 20111115
|
20
71
|
* Removed all references to the version4 API.
|
data/README.rdoc
CHANGED
@@ -54,6 +54,14 @@ shouting but I wanted to make sure this stood out.
|
|
54
54
|
|
55
55
|
All example code has been updated to use the new Ruby API.
|
56
56
|
|
57
|
+
== Breaking API Change from 0.9.3 to 0.9.5
|
58
|
+
|
59
|
+
The 0mq library functions zmq_send() and zmq_recv() were changed to
|
60
|
+
zmq_sendmsg() and zmq_recvmsg() with the 0mq 3.x API change. I have changed
|
61
|
+
the binding to always use #sendmsg and #recvmsg. This breaks existing code.
|
62
|
+
This should be the last breaking change heading into the 1.0 release of these
|
63
|
+
bindings.
|
64
|
+
|
57
65
|
== PERFORMANCE
|
58
66
|
|
59
67
|
Check out the latest performance results:
|
@@ -93,13 +101,14 @@ All features are implemented.
|
|
93
101
|
|
94
102
|
ctx = ZMQ::Context.new
|
95
103
|
s = ctx.socket ZMQ::REP
|
96
|
-
s.setsockopt(ZMQ::HWM, 100)
|
97
|
-
s.bind(bind_to)
|
104
|
+
rc = s.setsockopt(ZMQ::HWM, 100)
|
105
|
+
rc = s.bind(bind_to)
|
98
106
|
|
107
|
+
msg = ""
|
99
108
|
roundtrip_count.times do
|
100
|
-
|
109
|
+
rc = s.recv_string msg
|
101
110
|
raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
|
102
|
-
s.send_string msg, 0
|
111
|
+
rc = s.send_string msg, 0
|
103
112
|
end
|
104
113
|
|
105
114
|
|
@@ -119,15 +128,16 @@ All features are implemented.
|
|
119
128
|
|
120
129
|
ctx = ZMQ::Context.new
|
121
130
|
s = ctx.socket ZMQ::REQ
|
122
|
-
s.connect(connect_to)
|
131
|
+
rc = s.connect(connect_to)
|
123
132
|
|
124
133
|
msg = "#{ '3' * message_size }"
|
125
134
|
|
126
135
|
start_time = Time.now
|
127
136
|
|
137
|
+
msg = ""
|
128
138
|
roundtrip_count.times do
|
129
|
-
s.send_string msg, 0
|
130
|
-
|
139
|
+
rc = s.send_string msg, 0
|
140
|
+
rc = s.recv_string msg
|
131
141
|
raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
|
132
142
|
end
|
133
143
|
|
@@ -38,7 +38,7 @@ start_time = Time.now
|
|
38
38
|
|
39
39
|
# kick it off
|
40
40
|
message = ZMQ::Message.new("a" * message_size)
|
41
|
-
assert(s1.
|
41
|
+
assert(s1.sendmsg(message, ZMQ::NonBlocking))
|
42
42
|
|
43
43
|
i = roundtrip_count
|
44
44
|
|
@@ -49,8 +49,8 @@ until i.zero?
|
|
49
49
|
|
50
50
|
poller.readables.each do |socket|
|
51
51
|
received_message = ''
|
52
|
-
assert(socket.recv_string(received_message, ZMQ::
|
53
|
-
assert(socket.
|
52
|
+
assert(socket.recv_string(received_message, ZMQ::NonBlocking))
|
53
|
+
assert(socket.sendmsg(ZMQ::Message.new(received_message), ZMQ::NonBlocking))
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -28,13 +28,13 @@ assert(s.setsockopt(ZMQ::SUBSCRIBE, ""))
|
|
28
28
|
assert(s.bind(bind_to))
|
29
29
|
|
30
30
|
msg = ZMQ::Message.new
|
31
|
-
assert(s.
|
31
|
+
assert(s.recvmsg(msg))
|
32
32
|
|
33
33
|
start_time = Time.now
|
34
34
|
|
35
35
|
i = 1
|
36
36
|
while i < message_count
|
37
|
-
assert(s.
|
37
|
+
assert(s.recvmsg(msg))
|
38
38
|
i += 1
|
39
39
|
end
|
40
40
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
if ARGV.length != 3
|
5
|
+
puts "usage: ruby remote_throughput.rb <connect-to> <message-size> <message-count>"
|
6
|
+
Process.exit
|
7
|
+
end
|
8
|
+
|
9
|
+
connect_to = ARGV[0]
|
10
|
+
message_size = ARGV[1].to_i
|
11
|
+
message_count = ARGV[2].to_i
|
12
|
+
|
13
|
+
def assert(rc)
|
14
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
ctx = ZMQ::Context.new
|
19
|
+
s = ZMQ::Socket.new(ctx.pointer, ZMQ::PUB)
|
20
|
+
rescue ContextError => e
|
21
|
+
STDERR.puts "Could not allocate a context or socket!"
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
|
25
|
+
#assert(s.setsockopt(ZMQ::LINGER, 1_000))
|
26
|
+
#assert(s.setsockopt(ZMQ::RCVHWM, 0))
|
27
|
+
assert(s.setsockopt(ZMQ::HWM, 10))
|
28
|
+
assert(s.bind(connect_to))
|
29
|
+
|
30
|
+
# the sleep gives the downstream SUB socket a chance to register its
|
31
|
+
# subscription filters with this PUB socket
|
32
|
+
puts "Hit any key to start publishing"
|
33
|
+
STDIN.gets
|
34
|
+
|
35
|
+
i = 0
|
36
|
+
while i < message_count
|
37
|
+
msg = ZMQ::Message.new(i.to_s)
|
38
|
+
assert(s.sendmsg(msg))
|
39
|
+
puts i
|
40
|
+
i += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
sleep 10
|
44
|
+
assert(s.close)
|
45
|
+
|
46
|
+
ctx.terminate
|
@@ -71,7 +71,7 @@ assert(s4.recv_string(body)) if s4.more_parts?
|
|
71
71
|
puts "s4 received topic [#{topic}], body [#{body}]"
|
72
72
|
|
73
73
|
s5_string = ''
|
74
|
-
rc = s5.recv_string(s5_string, ZMQ::
|
74
|
+
rc = s5.recv_string(s5_string, ZMQ::NonBlocking)
|
75
75
|
eagain = (rc == -1 && ZMQ::Util.errno == ZMQ::EAGAIN)
|
76
76
|
puts(eagain ? "s5 received no messages" : "s5 FAILED")
|
77
77
|
|
@@ -38,7 +38,7 @@ until @done do
|
|
38
38
|
payload = "#{ '3' * 1024 }"
|
39
39
|
|
40
40
|
puts "sending payload nonblocking"
|
41
|
-
assert(s1.send_string(payload, ZMQ::
|
41
|
+
assert(s1.send_string(payload, ZMQ::NonBlocking))
|
42
42
|
@unsent = false
|
43
43
|
end
|
44
44
|
|
@@ -46,7 +46,7 @@ until @done do
|
|
46
46
|
if Time.now - start_time > 1
|
47
47
|
poller.readables.each do |sock|
|
48
48
|
received_msg = ''
|
49
|
-
assert(sock.recv_string(received_msg, ZMQ::
|
49
|
+
assert(sock.recv_string(received_msg, ZMQ::NonBlocking))
|
50
50
|
|
51
51
|
puts "message received [#{received_msg}]"
|
52
52
|
@done = true
|
@@ -27,8 +27,8 @@ payload = "#{ '3' * 2048 }"
|
|
27
27
|
sent_msg = ZMQ::Message.new(payload)
|
28
28
|
received_msg = ZMQ::Message.new
|
29
29
|
|
30
|
-
assert(s1.
|
31
|
-
assert(s2.
|
30
|
+
assert(s1.sendmsg(sent_msg))
|
31
|
+
assert(s2.recvmsg(received_msg))
|
32
32
|
|
33
33
|
result = payload == received_msg.copy_out_string ? "Request received" : "Received wrong payload"
|
34
34
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
#if ARGV.length != 3
|
5
|
+
# puts "usage: ruby local_throughtput.rb <bind-to> <message-size> <message-count>"
|
6
|
+
# Process.exit
|
7
|
+
#end
|
8
|
+
p ZMQ::Util.version
|
9
|
+
|
10
|
+
def assert(rc)
|
11
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
12
|
+
end
|
13
|
+
|
14
|
+
bind_to = ARGV[0]
|
15
|
+
message_size = ARGV[1].to_i
|
16
|
+
message_count = ARGV[2].to_i
|
17
|
+
sleep_time = ARGV[3].to_f
|
18
|
+
|
19
|
+
begin
|
20
|
+
ctx = ZMQ::Context.new
|
21
|
+
s = ZMQ::Socket.new(ctx.pointer, ZMQ::SUB)
|
22
|
+
rescue ContextError => e
|
23
|
+
STDERR.puts "Failed to allocate context or socket!"
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
#assert(s.setsockopt(ZMQ::LINGER, 100))
|
28
|
+
assert(s.setsockopt(ZMQ::IDENTITY, rand(999_999).to_s))
|
29
|
+
assert(s.setsockopt(ZMQ::SUBSCRIBE, ""))
|
30
|
+
assert(s.setsockopt(ZMQ::HWM, 1))
|
31
|
+
#assert(s.setsockopt(ZMQ::RCVHWM, 0))
|
32
|
+
#assert(s.setsockopt(ZMQ::SNDHWM, 0))
|
33
|
+
|
34
|
+
assert(s.connect(bind_to))
|
35
|
+
sleep 1
|
36
|
+
|
37
|
+
msg = ZMQ::Message.new
|
38
|
+
msg = ''
|
39
|
+
assert(s.recv_string(msg))
|
40
|
+
raise unless msg.to_i == 0
|
41
|
+
|
42
|
+
start_time = Time.now
|
43
|
+
|
44
|
+
i = 1
|
45
|
+
while i < message_count - 1
|
46
|
+
assert(s.recv_string(msg))
|
47
|
+
msg_i = msg.to_i
|
48
|
+
missed = (msg_i - i) - 1
|
49
|
+
puts "missed [#{missed}] messages" if missed > 0
|
50
|
+
i = msg_i
|
51
|
+
|
52
|
+
start = Time.now
|
53
|
+
while (Time.now - start) < sleep_time
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end_time = Time.now
|
58
|
+
|
59
|
+
elapsed = (end_time.to_f - start_time.to_f) * 1000000
|
60
|
+
if elapsed == 0
|
61
|
+
elapsed = 1
|
62
|
+
end
|
63
|
+
|
64
|
+
throughput = message_count * 1000000 / elapsed
|
65
|
+
megabits = throughput * message_size * 8 / 1000000
|
66
|
+
|
67
|
+
puts "message size: %i [B]" % message_size
|
68
|
+
puts "message count: %i" % message_count
|
69
|
+
puts "mean throughput: %i [msg/s]" % throughput
|
70
|
+
puts "mean throughput: %.3f [Mb/s]" % megabits
|
71
|
+
|
72
|
+
assert(s.close)
|
73
|
+
|
74
|
+
ctx.terminate
|
@@ -58,11 +58,11 @@ class Receiver
|
|
58
58
|
|
59
59
|
def run
|
60
60
|
msg = ZMQ::Message.new
|
61
|
-
assert(@socket.
|
61
|
+
assert(@socket.recvmsg(msg))
|
62
62
|
|
63
63
|
elapsed = elapsed_microseconds do
|
64
64
|
(@count -1).times do
|
65
|
-
assert(@socket.
|
65
|
+
assert(@socket.recvmsg(msg))
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -110,7 +110,7 @@ class Transmitter
|
|
110
110
|
i = 0
|
111
111
|
while i < @count
|
112
112
|
msg = ZMQ::Message.new(contents)
|
113
|
-
assert(@socket.
|
113
|
+
assert(@socket.sendmsg(msg))
|
114
114
|
i += 1
|
115
115
|
end
|
116
116
|
|
@@ -43,7 +43,7 @@ until @done do
|
|
43
43
|
|
44
44
|
5.times do |i|
|
45
45
|
payload = "#{ i.to_s * 40 }"
|
46
|
-
assert(s1.send_string(payload, ZMQ::
|
46
|
+
assert(s1.send_string(payload, ZMQ::NonBlocking))
|
47
47
|
end
|
48
48
|
@unsent = false
|
49
49
|
end
|
@@ -54,12 +54,12 @@ until @done do
|
|
54
54
|
|
55
55
|
if sock.identity =~ /xrep/
|
56
56
|
routing_info = ''
|
57
|
-
assert(sock.recv_string(routing_info, ZMQ::
|
57
|
+
assert(sock.recv_string(routing_info, ZMQ::NonBlocking))
|
58
58
|
puts "routing_info received [#{routing_info}] on socket.identity [#{sock.identity}]"
|
59
59
|
else
|
60
60
|
routing_info = nil
|
61
61
|
received_msg = ''
|
62
|
-
assert(sock.recv_string(received_msg, ZMQ::
|
62
|
+
assert(sock.recv_string(received_msg, ZMQ::NonBlocking))
|
63
63
|
|
64
64
|
# skip to the next iteration if received_msg is nil; that means we got an EAGAIN
|
65
65
|
next unless received_msg
|
@@ -68,13 +68,13 @@ until @done do
|
|
68
68
|
|
69
69
|
while sock.more_parts? do
|
70
70
|
received_msg = ''
|
71
|
-
assert(sock.recv_string(received_msg, ZMQ::
|
71
|
+
assert(sock.recv_string(received_msg, ZMQ::NonBlocking))
|
72
72
|
|
73
73
|
puts "message received [#{received_msg}]"
|
74
74
|
end
|
75
75
|
|
76
76
|
puts "kick back a reply"
|
77
|
-
assert(sock.send_string(routing_info, ZMQ::SNDMORE | ZMQ::
|
77
|
+
assert(sock.send_string(routing_info, ZMQ::SNDMORE | ZMQ::NonBlocking)) if routing_info
|
78
78
|
time = Time.now.strftime "%Y-%m-%dT%H:%M:%S.#{Time.now.usec}"
|
79
79
|
reply = "reply " + sock.identity.upcase + " #{time}"
|
80
80
|
puts "sent reply [#{reply}], #{time}"
|
@@ -38,7 +38,7 @@ start_time = Time.now
|
|
38
38
|
|
39
39
|
# kick it off
|
40
40
|
message = ZMQ::Message.new("a" * message_size)
|
41
|
-
assert(s1.sendmsg(message, ZMQ::
|
41
|
+
assert(s1.sendmsg(message, ZMQ::NonBlocking))
|
42
42
|
|
43
43
|
i = roundtrip_count
|
44
44
|
|
@@ -49,8 +49,8 @@ until i.zero?
|
|
49
49
|
|
50
50
|
poller.readables.each do |socket|
|
51
51
|
received_message = ''
|
52
|
-
assert(socket.recv_string(received_message, ZMQ::
|
53
|
-
assert(socket.sendmsg(ZMQ::Message.new(received_message), ZMQ::
|
52
|
+
assert(socket.recv_string(received_message, ZMQ::NonBlocking))
|
53
|
+
assert(socket.sendmsg(ZMQ::Message.new(received_message), ZMQ::NonBlocking))
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
if ARGV.length != 3
|
5
|
+
puts "usage: ruby remote_throughput.rb <connect-to> <message-size> <message-count>"
|
6
|
+
Process.exit
|
7
|
+
end
|
8
|
+
|
9
|
+
connect_to = ARGV[0]
|
10
|
+
message_size = ARGV[1].to_i
|
11
|
+
message_count = ARGV[2].to_i
|
12
|
+
|
13
|
+
def assert(rc)
|
14
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
ctx = ZMQ::Context.new
|
19
|
+
s = ZMQ::Socket.new(ctx.pointer, ZMQ::PUB)
|
20
|
+
rescue ContextError => e
|
21
|
+
STDERR.puts "Could not allocate a context or socket!"
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
|
25
|
+
#assert(s.setsockopt(ZMQ::LINGER, 1_000))
|
26
|
+
#assert(s.setsockopt(ZMQ::RCVHWM, 0))
|
27
|
+
assert(s.setsockopt(ZMQ::SNDHWM, 100))
|
28
|
+
assert(s.bind(connect_to))
|
29
|
+
|
30
|
+
# the sleep gives the downstream SUB socket a chance to register its
|
31
|
+
# subscription filters with this PUB socket
|
32
|
+
puts "Hit any key to start publishing"
|
33
|
+
STDIN.gets
|
34
|
+
|
35
|
+
i = 0
|
36
|
+
while i < message_count
|
37
|
+
msg = ZMQ::Message.new(i.to_s)
|
38
|
+
assert(s.sendmsg(msg))
|
39
|
+
puts i
|
40
|
+
i += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
sleep 10
|
44
|
+
assert(s.close)
|
45
|
+
|
46
|
+
ctx.terminate
|