ffi-rzmq 0.9.3 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|