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.
Files changed (56) hide show
  1. data/AUTHORS.txt +1 -0
  2. data/History.txt +35 -0
  3. data/README.rdoc +48 -15
  4. data/Rakefile +7 -2
  5. data/examples/README.rdoc +21 -76
  6. data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
  7. data/examples/v2api/local_lat_poll.rb +66 -0
  8. data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
  9. data/examples/v2api/publish_subscribe.rb +82 -0
  10. data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
  11. data/examples/v2api/remote_throughput.rb +39 -0
  12. data/examples/v2api/reqrep_poll.rb +62 -0
  13. data/examples/v2api/request_response.rb +40 -0
  14. data/examples/v2api/throughput_measurement.rb +138 -0
  15. data/examples/v3api/local_lat.rb +59 -0
  16. data/examples/v3api/local_lat_poll.rb +66 -0
  17. data/examples/v3api/local_throughput.rb +65 -0
  18. data/examples/v3api/publish_subscribe.rb +82 -0
  19. data/examples/v3api/remote_lat.rb +71 -0
  20. data/examples/v3api/remote_throughput.rb +47 -0
  21. data/examples/v3api/reqrep_poll.rb +62 -0
  22. data/examples/v3api/request_response.rb +40 -0
  23. data/examples/v3api/throughput_measurement.rb +166 -0
  24. data/ext/README +5 -0
  25. data/ffi-rzmq.gemspec +4 -4
  26. data/lib/ffi-rzmq.rb +4 -1
  27. data/lib/ffi-rzmq/constants.rb +178 -0
  28. data/lib/ffi-rzmq/context.rb +61 -45
  29. data/lib/ffi-rzmq/device.rb +22 -9
  30. data/lib/ffi-rzmq/exceptions.rb +0 -98
  31. data/lib/ffi-rzmq/libc.rb +19 -0
  32. data/lib/ffi-rzmq/libzmq.rb +188 -0
  33. data/lib/ffi-rzmq/message.rb +33 -40
  34. data/lib/ffi-rzmq/poll.rb +49 -52
  35. data/lib/ffi-rzmq/socket.rb +902 -392
  36. data/lib/ffi-rzmq/util.rb +101 -0
  37. data/spec/context_spec.rb +47 -21
  38. data/spec/device_spec.rb +78 -58
  39. data/spec/message_spec.rb +90 -12
  40. data/spec/multipart_spec.rb +162 -0
  41. data/spec/nonblocking_recv_spec.rb +325 -0
  42. data/spec/pushpull_spec.rb +95 -34
  43. data/spec/reqrep_spec.rb +55 -20
  44. data/spec/socket_spec.rb +353 -204
  45. data/spec/spec_helper.rb +46 -3
  46. data/version.txt +1 -1
  47. metadata +91 -66
  48. data/examples/local_lat_poll.rb +0 -54
  49. data/examples/local_lat_zerocopy.rb +0 -24
  50. data/examples/publish_subscribe.rb +0 -52
  51. data/examples/remote_lat_zerocopy.rb +0 -35
  52. data/examples/remote_throughput.rb +0 -27
  53. data/examples/reqrep_poll.rb +0 -49
  54. data/examples/request_response.rb +0 -23
  55. data/lib/ffi-rzmq/wrapper.rb +0 -121
  56. data/lib/ffi-rzmq/zmq.rb +0 -198
@@ -16,3 +16,4 @@ Stefan Kaes, github: skaes
16
16
 
17
17
  Dmitry Ustalov, github: eveel
18
18
 
19
+ Patrik Sundberg, github: sundbp
@@ -1,3 +1,38 @@
1
+ == 0.9.0 / 20110930
2
+ * Changed the behavior of every method that used to produce exceptions.
3
+ The methods now behave more like the C API functions. They return
4
+ result codes instead of raising exceptions. Further, the "receive"
5
+ methods on Socket now all take an empty string as a buffer to read
6
+ the message into.
7
+ This is a BREAKING CHANGE and is NOT backward compatible with earlier
8
+ releases. I apologize for the inconvenience, but this API will be
9
+ much easier to test/spec and maintain. It will also allow for the
10
+ production of more logical code.
11
+
12
+ * Major refactoring of Socket internals so that a single gem can
13
+ support libzmq 2.x, 3.x and 4.x APIs without any user intervention.
14
+ The correct libzmq version is detected at runtime and used to
15
+ configure the Ruby classes to conform to the proper API.
16
+
17
+ * Added Socket#recvmsgs as a convenience method for receiving a
18
+ multipart message into an array of Messages.
19
+
20
+ * Added support for new 0mq API introduced in the 3.0 branch.
21
+ API mostly changed for sending and receiving messages with new
22
+ POSIX-compliant send() and recv() functions. The original
23
+ functions were renamed sendmsg() and recvmsg().
24
+ Additionally, most getsockopt() and setsockopt() calls now use
25
+ an int (4 bytes) instead of a mish-mash of 32-bit and 64-bit
26
+ values.
27
+ For a full list of differences, visit the 0mq wiki page at:
28
+ http://www.zeromq.org/docs:3-0-upgrade
29
+
30
+ * Created a new ext/ directory so that users can copy the libzmq*
31
+ library files directly into the gem for easier distribution. This
32
+ path is checked *before* the usual system paths.
33
+
34
+ * Rewrote all examples to use the revised API.
35
+
1
36
  == 0.8.2 / 20110728
2
37
  * Fixed major bug with Socket#setsockopt when writing 8-byte longs.
3
38
 
@@ -9,12 +9,44 @@ 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.
14
+ The gem auto-configures itself to expose the API conforming to the loaded
15
+ C library.
16
+
12
17
  The impetus behind this library was to provide support for ZeroMQ in
13
- JRuby which has native threads. Unlike MRI, IronRuby and
14
- Rubinius which all have a GIL, JRuby allows for threaded access to ruby
15
- code from outside extensions. ZeroMQ is heavily threaded, so until the
16
- other runtimes remove their GIL, JRuby will likely be the best
17
- environment to run this library.
18
+ JRuby which has native threads. Unlike MRI, which has a GIL, JRuby and
19
+ Rubinius allow for threaded access to Ruby code from outside extensions.
20
+ ZeroMQ is heavily threaded, so until the MRI runtime removes its GIL,
21
+ JRuby and Rubinius will likely be the best environments to run this library.
22
+
23
+ == Breaking API Changes from 0.8.x to 0.9.0
24
+
25
+ There has been a major change in API from the 0.8.x series to 0.9.0.
26
+ Previously the Socket#send and Socket#recv methods could raise exceptions
27
+ when there was an error. They also returned true or false depending on
28
+ the success of failure of the operation. Mixing and matching return codes
29
+ and exceptions is a terrible idea in practice, so all of the exception
30
+ code from Socket has been removed.
31
+
32
+ The Poller and Message class have also had their exceptions removed for
33
+ returning errors on instance methods.
34
+
35
+ Unfortunately, it isn't possible to indicate that a call to #new has failed
36
+ by returning nil. A call to #new that fails needs to raise an exception to
37
+ indicate failure. (This isn't strictly true; this behavior can be changed
38
+ but it is not a standard Ruby idiom and is therefore a surprising, and
39
+ poor, thing to do.) Classes that formerly raised exceptions during allocation
40
+ now offer a factory method #create that will either return a successfully
41
+ constructed object or nil. Code should check for nil returns before using
42
+ the object.
43
+
44
+ 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. If you
46
+ prefer the original API, it should be relatively simple to wrap these
47
+ classes up to provide the original API.
48
+
49
+ All example code has been updated to use the new Ruby API.
18
50
 
19
51
  == PERFORMANCE
20
52
 
@@ -24,19 +56,22 @@ Check out the latest performance results:
24
56
 
25
57
  == FEATURES/PROBLEMS:
26
58
 
27
- This gem needs more tests. I'm certain there are a
28
- ton of bugs, so please open issues for them here or fork this project,
29
- fix them, and send me a pull request.
59
+ This gem needs more tests. This gem has been battle tested by myself
60
+ and others for over a year, so I am fairly confident that it is solid.
61
+ However, it is inevitable that there will be bugs, so please open
62
+ issues for them here or fork this project, fix them, and send me a pull
63
+ request.
30
64
 
31
65
  The 'ffi' gem has dropped support for MRI 1.8.x. Since this project relies
32
66
  on that gem to load and run this code, then this project also no longer
33
- supports MRI 1.8.x.
67
+ supports MRI 1.8.x. I recommend JRuby for the best performance and
68
+ stability.
34
69
 
35
70
  All features are implemented.
36
71
 
37
72
  == SYNOPSIS:
38
73
 
39
- Client code:
74
+ 0mq API v2 client code:
40
75
 
41
76
  require 'rubygems'
42
77
  require 'ffi-rzmq'
@@ -61,10 +96,8 @@ Client code:
61
96
  s.send_string msg, 0
62
97
  end
63
98
 
64
- # give the lib time to flush any remaining messages
65
- sleep 1
66
99
 
67
- Server code:
100
+ 0mq API v2 server code:
68
101
 
69
102
  require 'rubygems'
70
103
  require 'ffi-rzmq'
@@ -102,7 +135,7 @@ I highly recommend visiting the Learn Ruby 0mq project for a bunch of good code
102
135
 
103
136
  == REQUIREMENTS:
104
137
 
105
- * 0mq 2.0.10 or 2.1+
138
+ * 0mq 2.1.x or later; 2.0.x is no longer supported
106
139
 
107
140
  The ZeroMQ library must be installed on your system in a well-known location
108
141
  like /usr/local/lib. This is the default for new ZeroMQ installs.
@@ -140,7 +173,7 @@ To build from git master:
140
173
 
141
174
  (The MIT License)
142
175
 
143
- Copyright (c) 2010 Chuck Remes
176
+ Copyright (c) 2011 Chuck Remes
144
177
 
145
178
  Permission is hereby granted, free of charge, to any person obtaining
146
179
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -13,11 +13,16 @@ namespace :win do
13
13
  rm_rf PKG_PATH
14
14
  system "gem build #{NAME}.gemspec"
15
15
  mkdir_p PKG_PATH
16
- mv "#{NAME}-0.7.1.gem", PKG_PATH
17
- system "gem install #{PKG_PATH}/#{NAME}-0.7.1.gem"
16
+ mv "#{NAME}-*.gem", PKG_PATH
17
+ system "gem install #{PKG_PATH}/#{NAME}-*.gem"
18
18
  end
19
19
  end
20
20
 
21
+ require 'rspec/core/rake_task'
22
+ RSpec::Core::RakeTask.new(:spec)
23
+
24
+ task :default => :spec
25
+
21
26
  Bones {
22
27
  name 'ffi-rzmq'
23
28
  authors 'Chuck Remes'
@@ -2,17 +2,21 @@
2
2
 
3
3
  == Requirements
4
4
 
5
- 1. Installed gem
5
+ 1. lib dir
6
6
 
7
- All of the examples assume the gem has been successfully installed.
7
+ All of the examples assume the lib directory containing the gem sources is two directories up from the location of the example.
8
8
 
9
9
  2. Installed libzmq library
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
- 3. Two terminal windows
13
+ This gem auto-configures itself to conform to the API for 0mq 2.1.x, 3.x and 4.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
- ZeroMQ is used to build network applications. At minimum, there is a "client" application and a "server" application that talk to each other over the network, IPC or an internal thread queue. For the sake of code simplicity, these programs are in separate files and need to be executed in different windows.
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
+
17
+ 3. One terminal window
18
+
19
+ ZeroMQ is used to build network applications. At minimum, there is a "client" application and a "server" application that talk to each other over the network, IPC or an internal thread queue. Several of the examples start the client and server components within separate threads or use the polling mechanism to interleave I/O operations amongst several sockets. A few examples need two terminal windows because the client and server code is in separate files (local_lat.rb/remote_lat.rb, local_throughput.rb/remote_throughput.rb).
16
20
 
17
21
  == Latency Test
18
22
 
@@ -20,34 +24,26 @@ The examples include a latency performance test. The example sets up a pair of R
20
24
 
21
25
  ==== Files
22
26
 
23
- * local_lat.rb
24
- * remote_lat.rb
27
+ * latency_measurement.rb
25
28
 
26
29
  ==== Arguments
27
30
 
28
31
  The remote_lat.rb program takes 3 arguments:
29
32
 
30
- [bind_to] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
33
+ [link_address] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
31
34
 
32
35
  [message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
33
36
 
34
37
  [message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
35
38
 
36
-
37
- The local_lat.rb program also takes 3 arguments. They should exactly mirror the arguments given to remote_lat.rb.
38
-
39
39
 
40
40
  ==== Execution
41
41
 
42
- In one of the terminals, start up the remote_lat.rb program first. It *must* be launched first so that it can be ready and waiting for the first message sent by the local_lat.rb program.
43
-
44
- % ruby remote_lat.rb tcp://127.0.0.1:5555 1024 100_000
42
+ In an open terminal window, execute the latency_measurement.rb file.
45
43
 
46
- In the second terminal window, start up the local_lat.rb program too.
44
+ % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 100_000
47
45
 
48
- % ruby local_lat.rb tcp://127.0.0.1:5555 1024 100_000
49
-
50
- On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the remote_lat.rb program prints out a few statistics and exits. The local_lat.rb program does not print any message when it exits.
46
+ On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the program prints out a few statistics and exits.
51
47
 
52
48
  Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 10 million is better for determining a true measure.
53
49
 
@@ -55,87 +51,36 @@ On a desktop computer purchased in 2007, all of the Ruby runtimes report a laten
55
51
 
56
52
 
57
53
 
58
- == Zero Copy Latency Test
59
-
60
- The examples include a zero copy latency performance test. The example sets up a pair of REQ/REP sockets and send a message back and forth as fast as possible. There is only a single message in flight at any given moment. The time required to send the message the requested number of times determines overall single-message latency for this type of socket.
61
-
62
- The difference between this test and the first latency test has to do with the management of the sent and received messages. This test does not examine or copy the contents of the message at all. Instead, its mere presence is sufficient to echo it back to the other program. Therefore, this test is doing a lot less work for each message send & receive than the first latency test. Also, this test more perfectly mirrors the work being performed by the C latency test.
63
-
64
- ==== Files
65
-
66
- * local_lat_zerocopy.rb
67
- * remote_lat_zerocopy.rb
68
-
69
- ==== Arguments
70
-
71
- The remote_lat_zerocopy.rb program takes 3 arguments:
72
-
73
- [bind_to] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
74
-
75
- [message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
76
-
77
- [message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
78
-
79
-
80
- The local_lat_zerocopy.rb program also takes 3 arguments. They should exactly mirror the arguments given to remote_lat_zerocopy.rb.
81
-
82
-
83
- ==== Execution
84
-
85
- In one of the terminals, start up the remote_lat_zerocopy.rb program first. It *must* be launched first so that it can be ready and waiting for the first message sent by the local_lat_zerocopy.rb program.
86
-
87
- % ruby remote_lat_zerocopy.rb tcp://127.0.0.1:5555 1024 100_000
88
-
89
- In the second terminal window, start up the local_lat.rb program too.
90
-
91
- % ruby local_lat_zerocopy.rb tcp://127.0.0.1:5555 1024 100_000
92
-
93
- On a relatively new system, it can run 100k messages in under 30 seconds. When complete, the remote_lat_zerocopy.rb program prints out a few statistics and exits. The local_lat_zerocopy.rb program does not print any message when it exits.
94
-
95
- Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 10 million is better for determining a true measure.
96
-
97
- On a desktop computer purchased in 2007, all of the Ruby runtimes report a latency of approximately 95 microseconds per message. For comparison, the pure C latency test reports approximately 88 microseconds of latency.
98
-
99
-
100
-
101
54
  == Throughput Test
102
55
 
103
56
  The examples include a throughput performance test. The example sets up a pair of PUB/SUB sockets and publish messages as fast as possible to a subscriber listening for every message. The publisher can send much faster than the subscriber can retrieve messages.
104
57
 
105
- Since the publisher completes first, that program contains a "sleep" statement to keep the program alive and active which gives the subscriber more time to consume the queued messages. When the publisher exits, the socket closes and discards all remaining messages.
58
+ Since the publisher completes first, the program waits for all subscribers to exit before closing the PUB socket. This is necessary because all enqueued messages are discarded when the socket is closed.
106
59
 
107
60
  The subscriber prints some statistics when it exits.
108
61
 
109
62
  ==== Files
110
63
 
111
- * local_throughput.rb
112
- * remote_throughput.rb
64
+ * throughput_measurement.rb
113
65
 
114
66
  ==== Arguments
115
67
 
116
- The local_throughput.rb program takes 3 arguments:
68
+ The throughput_measurement.rb program takes 3 arguments:
117
69
 
118
- [bind_to] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
70
+ [link_address] Requires a transport string of the format "transport"://"endpoint"<:><port>. For example, tcp://127.0.0.1:5555
119
71
 
120
72
  [message size] Size of each message measured in bytes. Allowable range is 1 to 2^(64-1).
121
73
 
122
74
  [message count] The number of round-trips used for the latency measurements. Allowable range is 1 to 2^(64-1).
123
75
 
124
76
 
125
- The remote_throughput.rb program also takes 3 arguments. They should exactly mirror the arguments given to local_throughput.rb.
126
-
127
-
128
77
  ==== Execution
129
78
 
130
- In one of the terminals, start up the local_throughput.rb program first. It *must* be launched first so that it can be ready and waiting for the first message published by the remote_throughput.rb program.
79
+ In an open terminal, execute the throughput_measurement.rb script.
131
80
 
132
- % ruby local_throughput.rb tcp://127.0.0.1:5555 1024 100_000
81
+ % ruby throughput_measurement.rb tcp://127.0.0.1:5555 1024 100_000
133
82
 
134
- In the second terminal window, start up the second program.
135
-
136
- % ruby remote_throughput.rb tcp://127.0.0.1:5555 1024 100_000
137
-
138
- On a relatively new system, it can run 100k messages in under 10 seconds. When complete, the local_throughput.rb program prints out a few statistics and exits. The remote_throughput.rb program does not print any message when it exits.
83
+ On a relatively new system, it can run 100k messages in under 10 seconds. When complete, the program prints out a few statistics and exits.
139
84
 
140
85
  Running with a larger "message count" will yield a more accurate latency measurement since nearly all Ruby runtimes require a little warm up time to hit their stride. I recommend 100k as a minimum while 1 million is better for determining a true measure. NOTE! The publisher can send much faster than the subscriber so the publisher's queue will grow very rapidly in RAM. For 1 million messages (or more) this can consume hundreds of megabytes or gigabytes of RAM. On my system, sending 10 million messages requires 10 GB of RAM before the subscriber can catch up.
141
86
 
@@ -145,7 +90,7 @@ On a desktop computer purchased in 2007, all of the Ruby runtimes report a throu
145
90
 
146
91
  == Poll
147
92
 
148
- For a reasonable example of using zmq_poll(), take a look at the reqrep_poll.rb program. It illustrates the use of zmq_poll(), as wrapped by the Ruby library, for detecting and responding to read and write events recorded on sockets. It also shows how to use ZMQ::NO_BLOCK for non-blocking send and receive.
93
+ For a reasonable example of using zmq_poll(), take a look at the reqrep_poll.rb program. It illustrates the use of zmq_poll(), as wrapped by the Ruby library, for detecting and responding to read and write events recorded on sockets. It also shows how to use ZMQ::NO_BLOCK/ZMQ::DONTWAIT for non-blocking send and receive.
149
94
 
150
95
  ==== Files
151
96
 
@@ -16,11 +16,10 @@
16
16
  # You should have received a copy of the Lesser GNU General Public License
17
17
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
 
19
- require 'rubygems'
20
- require 'ffi-rzmq'
19
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
21
20
 
22
21
  if ARGV.length < 3
23
- puts "usage: local_lat <connect-to> <message-size> <roundtrip-count>"
22
+ puts "usage: ruby local_lat.rb <connect-to> <message-size> <roundtrip-count>"
24
23
  exit
25
24
  end
26
25
 
@@ -28,16 +27,32 @@ bind_to = ARGV[0]
28
27
  message_size = ARGV[1].to_i
29
28
  roundtrip_count = ARGV[2].to_i
30
29
 
31
- ctx = ZMQ::Context.new 1
32
- s = ctx.socket ZMQ::REP
33
- s.setsockopt(ZMQ::HWM, 100)
34
- s.bind(bind_to)
30
+ def assert(rc)
31
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
32
+ end
33
+
34
+ begin
35
+ ctx = ZMQ::Context.new
36
+ s = ctx.socket(ZMQ::REP)
37
+ rescue ContextError => e
38
+ STDERR.puts "Failed to allocate context or socket!"
39
+ raise
40
+ end
41
+
42
+ assert(s.setsockopt(ZMQ::LINGER, 100))
43
+ assert(s.setsockopt(ZMQ::HWM, 100))
44
+
45
+ assert(s.bind(bind_to))
35
46
 
36
47
  roundtrip_count.times do
37
- msg = s.recv_string 0
38
- raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
39
- s.send_string msg, 0
48
+ string = ''
49
+ assert(s.recv_string(string, 0))
50
+
51
+ raise "Message size doesn't match, expected [#{message_size}] but received [#{string.size}]" if message_size != string.size
52
+
53
+ assert(s.send_string(string, 0))
40
54
  end
41
55
 
42
- # give the lib time to flush any remaining messages
43
- sleep 1
56
+ assert(s.close)
57
+
58
+ ctx.terminate
@@ -0,0 +1,66 @@
1
+
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
3
+
4
+ if ARGV.length < 3
5
+ puts "usage: ruby local_lat.rb <connect-to> <message-size> <roundtrip-count>"
6
+ exit
7
+ end
8
+
9
+ link = ARGV[0]
10
+ message_size = ARGV[1].to_i
11
+ roundtrip_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
+ s1 = ctx.socket(ZMQ::REQ)
20
+ s2 = ctx.socket(ZMQ::REP)
21
+ rescue ContextError => e
22
+ STDERR.puts "Failed to allocate context or socket!"
23
+ raise
24
+ end
25
+
26
+ assert(s1.setsockopt(ZMQ::LINGER, 100))
27
+ assert(s2.setsockopt(ZMQ::LINGER, 100))
28
+
29
+ assert(s1.connect(link))
30
+ assert(s2.bind(link))
31
+
32
+ poller = ZMQ::Poller.new
33
+ poller.register_readable(s2)
34
+ poller.register_readable(s1)
35
+
36
+
37
+ start_time = Time.now
38
+
39
+ # kick it off
40
+ message = ZMQ::Message.new("a" * message_size)
41
+ assert(s1.send(message, ZMQ::NOBLOCK))
42
+
43
+ i = roundtrip_count
44
+
45
+ until i.zero?
46
+ i -= 1
47
+
48
+ assert(poller.poll_nonblock)
49
+
50
+ poller.readables.each do |socket|
51
+ received_message = ''
52
+ assert(socket.recv_string(received_message, ZMQ::NOBLOCK))
53
+ assert(socket.send(ZMQ::Message.new(received_message), ZMQ::NOBLOCK))
54
+ end
55
+ end
56
+
57
+ elapsed_usecs = (Time.now.to_f - start_time.to_f) * 1_000_000
58
+ latency = elapsed_usecs / roundtrip_count / 2
59
+
60
+ puts "mean latency: %.3f [us]" % latency
61
+ puts "received all messages in %.3f seconds" % (elapsed_usecs / 1_000_000)
62
+
63
+ assert(s1.close)
64
+ assert(s2.close)
65
+
66
+ ctx.terminate