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/.gitignore
ADDED
data/Gemfile
ADDED
data/History.txt
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
== 0.9.2 / 20111115
|
2
|
+
* Removed all references to the version4 API.
|
3
|
+
|
4
|
+
* Dropped support for 3.0.x and added support for 3.1.x. The 0mq
|
5
|
+
community has pretty much voted to abandon the path taken in 3.0
|
6
|
+
so the 3.1 branch is the API that will be supported.
|
7
|
+
|
8
|
+
* Fixed a bug in Poller#delete where it would erroneously return
|
9
|
+
false even when it successfully deleted a socket. Issue 46.
|
10
|
+
|
11
|
+
* All specs pass for 2.1.x API.
|
12
|
+
|
13
|
+
* 3 specs fail when run with 3.1 API; these are due to bugs in the
|
14
|
+
0mq library and are *not* ffi-rzmq bugs.
|
15
|
+
|
16
|
+
* Rescue LoadErrors when loading libzmq. Print a warning about
|
17
|
+
adding libzmq.dll to the Windows PATH for that platform. Print
|
18
|
+
the search paths where the gem looks for libzmq.
|
19
|
+
|
20
|
+
== 0.9.1 / 20111027
|
21
|
+
* Moved LibC and LibZMQ into the ZMQ module namespace. Necessary to
|
22
|
+
avoid namespace collisions with other libraries that also use
|
23
|
+
the constants LibC and/or LibZMQ.
|
24
|
+
|
25
|
+
* Fixed a bug where file descriptors registered on Poll were never
|
26
|
+
returned as readable or writable.
|
27
|
+
|
28
|
+
* Added Socket#recv_multipart. This returns the message body and
|
29
|
+
return address envelope as separate arrays. Only to be used with
|
30
|
+
XREQ/XREP/DEALER/ROUTER sockets.
|
31
|
+
|
1
32
|
== 0.9.0 / 20110930
|
2
33
|
* Changed the behavior of every method that used to produce exceptions.
|
3
34
|
The methods now behave more like the C API functions. They return
|
data/README.rdoc
CHANGED
@@ -9,10 +9,11 @@ function interface). It's a pure ruby wrapper so this gem can be loaded
|
|
9
9
|
and run by any ruby runtime that supports FFI. That's all of them:
|
10
10
|
MRI 1.9.x, Rubinius and JRuby.
|
11
11
|
|
12
|
-
This single gem supports 0mq 2.x and 3.x 0mq APIs. The 0mq project started
|
13
|
-
making backward-incompatible changes to the API with the 3.x release.
|
12
|
+
This single gem supports 0mq 2.x and 3.1.x 0mq APIs. The 0mq project started
|
13
|
+
making backward-incompatible changes to the API with the 3.1.x release.
|
14
14
|
The gem auto-configures itself to expose the API conforming to the loaded
|
15
|
-
C library.
|
15
|
+
C library. 0mq API 3.0 is *not* supported; the 0mq community voted to
|
16
|
+
abandon it.
|
16
17
|
|
17
18
|
The impetus behind this library was to provide support for ZeroMQ in
|
18
19
|
JRuby which has native threads. Unlike MRI, which has a GIL, JRuby and
|
@@ -25,9 +26,12 @@ JRuby and Rubinius will likely be the best environments to run this library.
|
|
25
26
|
There has been a major change in API from the 0.8.x series to 0.9.0.
|
26
27
|
Previously the Socket#send and Socket#recv methods could raise exceptions
|
27
28
|
when there was an error. They also returned true or false depending on
|
28
|
-
the success
|
29
|
-
|
30
|
-
|
29
|
+
the success or failure of the operation. The exceptions were also used
|
30
|
+
for managing flow control when doing non-blocking send/recv.
|
31
|
+
|
32
|
+
Mixing and matching return codes and exceptions is a terrible idea in
|
33
|
+
practice. Using them for flow control is an even worse sin. So all of
|
34
|
+
the exception code from Socket has been removed.
|
31
35
|
|
32
36
|
The Poller and Message class have also had their exceptions removed for
|
33
37
|
returning errors on instance methods.
|
@@ -42,9 +46,11 @@ constructed object or nil. Code should check for nil returns before using
|
|
42
46
|
the object.
|
43
47
|
|
44
48
|
This is a *breaking* change. I'm sorry but the old API's inconsistency was
|
45
|
-
causing too many problems. It now more closely mimics the 0mq C API.
|
46
|
-
|
47
|
-
|
49
|
+
causing too many problems. It now more closely mimics the 0mq C API. IF YOU
|
50
|
+
PREFER THE ORIGINAL API WHICH WAS CLOSER TO IDIOMATIC RUBY, IT SHOULD BE
|
51
|
+
EASY TO WRAP THESE CLASSES UP TO PROVIDE A MORE IDIOMATIC API. I SUGGEST
|
52
|
+
YOU CREATE A PATCH *OR* CREATE A SEPARATE GEM TO WRAP THIS ONE. Sorry for
|
53
|
+
shouting but I wanted to make sure this stood out.
|
48
54
|
|
49
55
|
All example code has been updated to use the new Ruby API.
|
50
56
|
|
@@ -135,7 +141,7 @@ I highly recommend visiting the Learn Ruby 0mq project for a bunch of good code
|
|
135
141
|
|
136
142
|
== REQUIREMENTS:
|
137
143
|
|
138
|
-
* 0mq 2.1.x or later; 2.0.x
|
144
|
+
* 0mq 2.1.x, 3.1.x or later; 2.0.x and 3.0.x are no longer supported
|
139
145
|
|
140
146
|
The ZeroMQ library must be installed on your system in a well-known location
|
141
147
|
like /usr/local/lib. This is the default for new ZeroMQ installs.
|
@@ -167,7 +173,11 @@ To build from git master:
|
|
167
173
|
% gem build ffi-rzmq.gemspec
|
168
174
|
% gem install ffi-rzmq-*.gem
|
169
175
|
|
170
|
-
|
176
|
+
|
177
|
+
NOTE for Windows users!
|
178
|
+
In order for this gem to find the libzmq.dll, it *must* be on the Windows PATH. Google
|
179
|
+
for "modify windows path" for instructions on how to do that if you are unfamiliar with
|
180
|
+
that activity.
|
171
181
|
|
172
182
|
== LICENSE:
|
173
183
|
|
data/Rakefile
CHANGED
@@ -1,38 +1,6 @@
|
|
1
|
-
|
2
|
-
require 'bones'
|
3
|
-
rescue LoadError
|
4
|
-
abort '### Please install the "bones" gem ###'
|
5
|
-
end
|
6
|
-
|
7
|
-
namespace :win do
|
8
|
-
|
9
|
-
desc 'Build and install gem under Windows. Mr Bones just has to break things using tar.'
|
10
|
-
task :install do
|
11
|
-
PKG_PATH = File.join(File.dirname(__FILE__), 'pkg')
|
12
|
-
NAME = File.basename(File.dirname(__FILE__))
|
13
|
-
rm_rf PKG_PATH
|
14
|
-
system "gem build #{NAME}.gemspec"
|
15
|
-
mkdir_p PKG_PATH
|
16
|
-
mv "#{NAME}-*.gem", PKG_PATH
|
17
|
-
system "gem install #{PKG_PATH}/#{NAME}-*.gem"
|
18
|
-
end
|
19
|
-
end
|
1
|
+
require 'bundler/gem_tasks'
|
20
2
|
|
21
3
|
require 'rspec/core/rake_task'
|
22
|
-
RSpec::Core::RakeTask.new
|
4
|
+
RSpec::Core::RakeTask.new
|
23
5
|
|
24
6
|
task :default => :spec
|
25
|
-
|
26
|
-
Bones {
|
27
|
-
name 'ffi-rzmq'
|
28
|
-
authors 'Chuck Remes'
|
29
|
-
email 'cremes@mac.com'
|
30
|
-
url 'http://github.com/chuckremes/ffi-rzmq'
|
31
|
-
readme_file 'README.rdoc'
|
32
|
-
ruby_opts.clear # turn off warnings
|
33
|
-
|
34
|
-
# necessary for MRI; unnecessary for JRuby and RBX
|
35
|
-
# can't enable this until JRuby & RBX have a way of dealing with it cleanly
|
36
|
-
#depend_on 'ffi', '>= 1.0.0'
|
37
|
-
}
|
38
|
-
|
data/examples/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ All of the examples assume the lib directory containing the gem sources is two d
|
|
10
10
|
|
11
11
|
The ZeroMQ C libraries need to be downloaded, compiled and installed separately from the gem. Please see http://www.zeromq.org/area:download for links to the downloadable files along with some simple installation instructions. Also, be sure to check the FAQ if you run into problems with compiling.
|
12
12
|
|
13
|
-
This gem auto-configures itself to conform to the API for 0mq 2.1.x
|
13
|
+
This gem auto-configures itself to conform to the API for 0mq 2.1.x and 3.1.x. The 0mq project started making backward-incompatible changes with the 3.x branch. Rather than create separate gems, this one handles all of them.
|
14
14
|
|
15
15
|
It is possible to install the libzmq* files directly into the gem in the ext/ directory. This directory is checked for loadable libraries first before it falls back to checking the system paths.
|
16
16
|
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
# Within a single process, we start up two threads. One thread has a REQ (request)
|
6
|
+
# socket and the second thread has a REP (reply) socket. We measure the
|
7
|
+
# *round-trip* latency between these sockets. Only *one* message is in flight at
|
8
|
+
# any given moment.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
#
|
18
|
+
|
19
|
+
if ARGV.length < 3
|
20
|
+
puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
link = ARGV[0]
|
25
|
+
message_size = ARGV[1].to_i
|
26
|
+
roundtrip_count = ARGV[2].to_i
|
27
|
+
|
28
|
+
def assert(rc)
|
29
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
master_context = ZMQ::Context.new
|
34
|
+
rescue ContextError => e
|
35
|
+
STDERR.puts "Failed to allocate context or socket!"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Receiver
|
41
|
+
def initialize context, link, size, count
|
42
|
+
@context = context
|
43
|
+
@link = link
|
44
|
+
@size = size
|
45
|
+
@count = count
|
46
|
+
|
47
|
+
begin
|
48
|
+
@socket = @context.socket(ZMQ::REP)
|
49
|
+
rescue ContextError => e
|
50
|
+
STDERR.puts "Failed to allocate REP socket!"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
55
|
+
|
56
|
+
assert(@socket.setsockopt(ZMQ::HWM, 100))
|
57
|
+
|
58
|
+
assert(@socket.bind(@link))
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
@count.times do
|
63
|
+
string = ''
|
64
|
+
assert(@socket.recv_string(string, 0))
|
65
|
+
|
66
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
|
67
|
+
|
68
|
+
assert(@socket.send_string(string, 0))
|
69
|
+
end
|
70
|
+
|
71
|
+
assert(@socket.close)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Transmitter
|
76
|
+
def initialize context, link, size, count
|
77
|
+
@context = context
|
78
|
+
@link = link
|
79
|
+
@size = size
|
80
|
+
@count = count
|
81
|
+
|
82
|
+
begin
|
83
|
+
@socket = @context.socket(ZMQ::REQ)
|
84
|
+
rescue ContextError => e
|
85
|
+
STDERR.puts "Failed to allocate REP socket!"
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
|
89
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
90
|
+
|
91
|
+
assert(@socket.setsockopt(ZMQ::HWM, 100))
|
92
|
+
|
93
|
+
assert(@socket.connect(@link))
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
msg = "#{ '3' * @size }"
|
98
|
+
|
99
|
+
elapsed = elapsed_microseconds do
|
100
|
+
@count.times do
|
101
|
+
assert(@socket.send_string(msg, 0))
|
102
|
+
assert(@socket.recv_string(msg, 0))
|
103
|
+
|
104
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
latency = elapsed / @count / 2
|
109
|
+
|
110
|
+
puts "message size: %i [B]" % @size
|
111
|
+
puts "roundtrip count: %i" % @count
|
112
|
+
puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
|
113
|
+
puts "mean latency: %.3f [us]" % latency
|
114
|
+
assert(@socket.close)
|
115
|
+
end
|
116
|
+
|
117
|
+
def elapsed_microseconds(&blk)
|
118
|
+
start = Time.now
|
119
|
+
yield
|
120
|
+
value = ((Time.now - start) * 1_000_000)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
threads = []
|
125
|
+
threads << Thread.new do
|
126
|
+
receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
|
127
|
+
receiver.run
|
128
|
+
end
|
129
|
+
|
130
|
+
sleep 1
|
131
|
+
|
132
|
+
threads << Thread.new do
|
133
|
+
transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
|
134
|
+
transmitter.run
|
135
|
+
end
|
136
|
+
|
137
|
+
threads.each {|t| t.join}
|
138
|
+
|
139
|
+
master_context.terminate
|
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
def assert(rc)
|
6
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
7
|
+
end
|
8
|
+
|
9
|
+
link = "tcp://127.0.0.1:5555"
|
10
|
+
|
11
|
+
|
12
|
+
begin
|
13
|
+
ctx = ZMQ::Context.new
|
14
|
+
s1 = ctx.socket(ZMQ::XREQ)
|
15
|
+
s2 = ctx.socket(ZMQ::XREP)
|
16
|
+
rescue ContextError => e
|
17
|
+
STDERR.puts "Failed to allocate context or socket"
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
|
21
|
+
s1.identity = 'socket1.xreq'
|
22
|
+
s2.identity = 'socket2.xrep'
|
23
|
+
|
24
|
+
assert(s1.setsockopt(ZMQ::LINGER, 100))
|
25
|
+
assert(s2.setsockopt(ZMQ::LINGER, 100))
|
26
|
+
|
27
|
+
assert(s1.bind(link))
|
28
|
+
assert(s2.connect(link))
|
29
|
+
|
30
|
+
poller = ZMQ::Poller.new
|
31
|
+
poller.register_readable(s2)
|
32
|
+
poller.register_writable(s1)
|
33
|
+
|
34
|
+
start_time = Time.now
|
35
|
+
@unsent = true
|
36
|
+
|
37
|
+
until @done do
|
38
|
+
assert(poller.poll_nonblock)
|
39
|
+
|
40
|
+
# send the message after 5 seconds
|
41
|
+
if Time.now - start_time > 5 && @unsent
|
42
|
+
puts "sending payload nonblocking"
|
43
|
+
|
44
|
+
5.times do |i|
|
45
|
+
payload = "#{ i.to_s * 40 }"
|
46
|
+
assert(s1.send_string(payload, ZMQ::NOBLOCK))
|
47
|
+
end
|
48
|
+
@unsent = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# check for messages after 1 second
|
52
|
+
if Time.now - start_time > 1
|
53
|
+
poller.readables.each do |sock|
|
54
|
+
|
55
|
+
if sock.identity =~ /xrep/
|
56
|
+
routing_info = ''
|
57
|
+
assert(sock.recv_string(routing_info, ZMQ::NOBLOCK))
|
58
|
+
puts "routing_info received [#{routing_info}] on socket.identity [#{sock.identity}]"
|
59
|
+
else
|
60
|
+
routing_info = nil
|
61
|
+
received_msg = ''
|
62
|
+
assert(sock.recv_string(received_msg, ZMQ::NOBLOCK))
|
63
|
+
|
64
|
+
# skip to the next iteration if received_msg is nil; that means we got an EAGAIN
|
65
|
+
next unless received_msg
|
66
|
+
puts "message received [#{received_msg}] on socket.identity [#{sock.identity}]"
|
67
|
+
end
|
68
|
+
|
69
|
+
while sock.more_parts? do
|
70
|
+
received_msg = ''
|
71
|
+
assert(sock.recv_string(received_msg, ZMQ::NOBLOCK))
|
72
|
+
|
73
|
+
puts "message received [#{received_msg}]"
|
74
|
+
end
|
75
|
+
|
76
|
+
puts "kick back a reply"
|
77
|
+
assert(sock.send_string(routing_info, ZMQ::SNDMORE | ZMQ::NOBLOCK)) if routing_info
|
78
|
+
time = Time.now.strftime "%Y-%m-%dT%H:%M:%S.#{Time.now.usec}"
|
79
|
+
reply = "reply " + sock.identity.upcase + " #{time}"
|
80
|
+
puts "sent reply [#{reply}], #{time}"
|
81
|
+
assert(sock.send_string(reply))
|
82
|
+
@done = true
|
83
|
+
poller.register_readable(s1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
puts "executed in [#{Time.now - start_time}] seconds"
|
89
|
+
|
90
|
+
assert(s1.close)
|
91
|
+
assert(s2.close)
|
92
|
+
|
93
|
+
ctx.terminate
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
3
|
+
|
4
|
+
|
5
|
+
# Within a single process, we start up two threads. One thread has a REQ (request)
|
6
|
+
# socket and the second thread has a REP (reply) socket. We measure the
|
7
|
+
# *round-trip* latency between these sockets. Only *one* message is in flight at
|
8
|
+
# any given moment.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
#
|
18
|
+
|
19
|
+
if ARGV.length < 3
|
20
|
+
puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
link = ARGV[0]
|
25
|
+
message_size = ARGV[1].to_i
|
26
|
+
roundtrip_count = ARGV[2].to_i
|
27
|
+
|
28
|
+
def assert(rc)
|
29
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
master_context = ZMQ::Context.new
|
34
|
+
rescue ContextError => e
|
35
|
+
STDERR.puts "Failed to allocate context or socket!"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Receiver
|
41
|
+
def initialize context, link, size, count
|
42
|
+
@context = context
|
43
|
+
@link = link
|
44
|
+
@size = size
|
45
|
+
@count = count
|
46
|
+
|
47
|
+
begin
|
48
|
+
@socket = @context.socket(ZMQ::REP)
|
49
|
+
rescue ContextError => e
|
50
|
+
STDERR.puts "Failed to allocate REP socket!"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
55
|
+
assert(@socket.setsockopt(ZMQ::RCVHWM, 100))
|
56
|
+
assert(@socket.setsockopt(ZMQ::SNDHWM, 100))
|
57
|
+
|
58
|
+
assert(@socket.bind(@link))
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
@count.times do
|
63
|
+
string = ''
|
64
|
+
assert(@socket.recv_string(string, 0))
|
65
|
+
|
66
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
|
67
|
+
|
68
|
+
assert(@socket.send_string(string, 0))
|
69
|
+
end
|
70
|
+
|
71
|
+
assert(@socket.close)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Transmitter
|
76
|
+
def initialize context, link, size, count
|
77
|
+
@context = context
|
78
|
+
@link = link
|
79
|
+
@size = size
|
80
|
+
@count = count
|
81
|
+
|
82
|
+
begin
|
83
|
+
@socket = @context.socket(ZMQ::REQ)
|
84
|
+
rescue ContextError => e
|
85
|
+
STDERR.puts "Failed to allocate REP socket!"
|
86
|
+
raise
|
87
|
+
end
|
88
|
+
|
89
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
90
|
+
assert(@socket.setsockopt(ZMQ::RCVHWM, 100))
|
91
|
+
assert(@socket.setsockopt(ZMQ::SNDHWM, 100))
|
92
|
+
|
93
|
+
assert(@socket.connect(@link))
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
msg = "#{ '3' * @size }"
|
98
|
+
|
99
|
+
elapsed = elapsed_microseconds do
|
100
|
+
@count.times do
|
101
|
+
assert(@socket.send_string(msg, 0))
|
102
|
+
assert(@socket.recv_string(msg, 0))
|
103
|
+
|
104
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
latency = elapsed / @count / 2
|
109
|
+
|
110
|
+
puts "message size: %i [B]" % @size
|
111
|
+
puts "roundtrip count: %i" % @count
|
112
|
+
puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
|
113
|
+
puts "mean latency: %.3f [us]" % latency
|
114
|
+
assert(@socket.close)
|
115
|
+
end
|
116
|
+
|
117
|
+
def elapsed_microseconds(&blk)
|
118
|
+
start = Time.now
|
119
|
+
yield
|
120
|
+
value = ((Time.now - start) * 1_000_000)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
threads = []
|
125
|
+
threads << Thread.new do
|
126
|
+
receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
|
127
|
+
receiver.run
|
128
|
+
end
|
129
|
+
|
130
|
+
sleep 1
|
131
|
+
|
132
|
+
threads << Thread.new do
|
133
|
+
transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
|
134
|
+
transmitter.run
|
135
|
+
end
|
136
|
+
|
137
|
+
threads.each {|t| t.join}
|
138
|
+
|
139
|
+
master_context.terminate
|