ffi-rzmq 0.8.2 → 0.9.0

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.
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
@@ -1,29 +1,40 @@
1
- require 'rubygems'
2
- require 'ffi-rzmq'
1
+
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
3
3
 
4
4
  if ARGV.length != 3
5
- puts "usage: local_thr <bind-to> <message-size> <message-count>"
5
+ puts "usage: ruby local_throughtput.rb <bind-to> <message-size> <message-count>"
6
6
  Process.exit
7
7
  end
8
8
 
9
+ def assert(rc)
10
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
11
+ end
12
+
9
13
  bind_to = ARGV[0]
10
14
  message_size = ARGV[1].to_i
11
15
  message_count = ARGV[2].to_i
12
16
 
13
- ctx = ZMQ::Context.new
14
- s = ZMQ::Socket.new ctx.pointer, ZMQ::SUB
15
- s.setsockopt ZMQ::SUBSCRIBE, ""
17
+ begin
18
+ ctx = ZMQ::Context.new
19
+ s = ZMQ::Socket.new(ctx.pointer, ZMQ::SUB)
20
+ rescue ContextError => e
21
+ STDERR.puts "Failed to allocate context or socket!"
22
+ raise
23
+ end
16
24
 
17
- s.bind bind_to
25
+ assert(s.setsockopt(ZMQ::LINGER, 100))
26
+ assert(s.setsockopt(ZMQ::SUBSCRIBE, ""))
27
+
28
+ assert(s.bind(bind_to))
18
29
 
19
30
  msg = ZMQ::Message.new
20
- rc = s.recv msg
31
+ assert(s.recv(msg))
21
32
 
22
33
  start_time = Time.now
23
34
 
24
35
  i = 1
25
36
  while i < message_count
26
- result_code = s.recv msg
37
+ assert(s.recv(msg))
27
38
  i += 1
28
39
  end
29
40
 
@@ -41,3 +52,7 @@ puts "message size: %i [B]" % message_size
41
52
  puts "message count: %i" % message_count
42
53
  puts "mean throughput: %i [msg/s]" % throughput
43
54
  puts "mean throughput: %.3f [Mb/s]" % megabits
55
+
56
+ assert(s.close)
57
+
58
+ ctx.terminate
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
2
+
3
+
4
+ def assert(rc)
5
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
6
+ end
7
+
8
+ link = "tcp://127.0.0.1:5555"
9
+
10
+ begin
11
+ ctx = ZMQ::Context.new
12
+ s1 = ctx.socket(ZMQ::PUB)
13
+ s2 = ctx.socket(ZMQ::SUB)
14
+ s3 = ctx.socket(ZMQ::SUB)
15
+ s4 = ctx.socket(ZMQ::SUB)
16
+ s5 = ctx.socket(ZMQ::SUB)
17
+ rescue ContextError => e
18
+ STDERR.puts "Failed to allocate context or socket!"
19
+ raise
20
+ end
21
+
22
+ assert(s1.setsockopt(ZMQ::LINGER, 100))
23
+ assert(s2.setsockopt(ZMQ::SUBSCRIBE, '')) # receive all
24
+ assert(s3.setsockopt(ZMQ::SUBSCRIBE, 'animals')) # receive any starting with this string
25
+ assert(s4.setsockopt(ZMQ::SUBSCRIBE, 'animals.dog'))
26
+ assert(s5.setsockopt(ZMQ::SUBSCRIBE, 'animals.cat'))
27
+
28
+ assert(s1.bind(link))
29
+ assert(s2.connect(link))
30
+ assert(s3.connect(link))
31
+ assert(s4.connect(link))
32
+ assert(s5.connect(link))
33
+
34
+ sleep 1
35
+
36
+ topic = "animals.dog"
37
+ payload = "Animal crackers!"
38
+
39
+ s1.identity = "publisher-A"
40
+ puts "sending"
41
+ # use the new multi-part messaging support to
42
+ # automatically separate the topic from the body
43
+ assert(s1.send_string(topic, ZMQ::SNDMORE))
44
+ assert(s1.send_string(payload, ZMQ::SNDMORE))
45
+ assert(s1.send_string(s1.identity))
46
+
47
+ topic = ''
48
+ assert(s2.recv_string(topic))
49
+
50
+ body = ''
51
+ assert(s2.recv_string(body)) if s2.more_parts?
52
+
53
+ identity = ''
54
+ assert(s2.recv_string(identity)) if s2.more_parts?
55
+ puts "s2 received topic [#{topic}], body [#{body}], identity [#{identity}]"
56
+
57
+
58
+
59
+ topic = ''
60
+ assert(s3.recv_string(topic))
61
+
62
+ body = ''
63
+ assert(s3.recv_string(body)) if s3.more_parts?
64
+ puts "s3 received topic [#{topic}], body [#{body}]"
65
+
66
+ topic = ''
67
+ assert(s4.recv_string(topic))
68
+
69
+ body = ''
70
+ assert(s4.recv_string(body)) if s4.more_parts?
71
+ puts "s4 received topic [#{topic}], body [#{body}]"
72
+
73
+ s5_string = ''
74
+ rc = s5.recv_string(s5_string, ZMQ::NOBLOCK)
75
+ eagain = (rc == -1 && ZMQ::Util.errno == ZMQ::EAGAIN)
76
+ puts(eagain ? "s5 received no messages" : "s5 FAILED")
77
+
78
+ [s1, s2, s3, s4, s5].each do |socket|
79
+ assert(socket.close)
80
+ end
81
+
82
+ ctx.terminate
@@ -16,29 +16,43 @@
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
+
20
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
21
21
 
22
22
  if ARGV.length < 3
23
- puts "usage: remote_lat <connect-to> <message-size> <roundtrip-count>"
23
+ puts "usage: ruby remote_lat.rb <connect-to> <message-size> <roundtrip-count>"
24
24
  exit
25
25
  end
26
26
 
27
+ def assert(rc)
28
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
29
+ end
30
+
27
31
  connect_to = ARGV[0]
28
32
  message_size = ARGV[1].to_i
29
33
  roundtrip_count = ARGV[2].to_i
30
34
 
31
- ctx = ZMQ::Context.new 1
32
- s = ctx.socket ZMQ::REQ
33
- s.connect(connect_to)
35
+ begin
36
+ ctx = ZMQ::Context.new
37
+ s = ctx.socket(ZMQ::REQ)
38
+ rescue ContextError => e
39
+ STDERR.puts "Failed to allocate context or socket!"
40
+ raise
41
+ end
42
+
43
+ assert(s.setsockopt(ZMQ::LINGER, 100))
44
+ assert(s.connect(connect_to))
34
45
 
35
46
  msg = "#{ '3' * message_size }"
36
47
 
37
48
  start_time = Time.now
38
49
 
39
50
  roundtrip_count.times do
40
- s.send_string msg, 0
41
- msg = s.recv_string 0
51
+ assert(s.send_string(msg, 0))
52
+
53
+ msg = ''
54
+ assert(s.recv_string(msg, 0))
55
+
42
56
  raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
43
57
  end
44
58
 
@@ -51,3 +65,7 @@ puts "message size: %i [B]" % message_size
51
65
  puts "roundtrip count: %i" % roundtrip_count
52
66
  puts "throughput (msgs/s): %i" % (roundtrip_count / elapsed_secs)
53
67
  puts "mean latency: %.3f [us]" % latency
68
+
69
+ assert(s.close)
70
+
71
+ ctx.terminate
@@ -0,0 +1,39 @@
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.connect(connect_to))
27
+
28
+ contents = "#{'0'*message_size}"
29
+
30
+ i = 0
31
+ while i < message_count
32
+ msg = ZMQ::Message.new(contents)
33
+ assert(s.send(msg))
34
+ i += 1
35
+ end
36
+
37
+ assert(s.close)
38
+
39
+ ctx.terminate
@@ -0,0 +1,62 @@
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:5554"
10
+
11
+ begin
12
+ ctx = ZMQ::Context.new
13
+ s1 = ctx.socket(ZMQ::REQ)
14
+ s2 = ctx.socket(ZMQ::REP)
15
+ rescue ContextError => e
16
+ STDERR.puts "Failed to allocate context or socket!"
17
+ raise
18
+ end
19
+
20
+ assert(s1.setsockopt(ZMQ::LINGER, 100))
21
+ assert(s2.setsockopt(ZMQ::LINGER, 100))
22
+
23
+ assert(s1.connect(link))
24
+ assert(s2.bind(link))
25
+
26
+ poller = ZMQ::Poller.new
27
+ poller.register_readable(s2)
28
+ poller.register_writable(s1)
29
+
30
+ start_time = Time.now
31
+ @unsent = true
32
+
33
+ until @done do
34
+ assert(poller.poll_nonblock)
35
+
36
+ # send the message after 5 seconds
37
+ if Time.now - start_time > 5 && @unsent
38
+ payload = "#{ '3' * 1024 }"
39
+
40
+ puts "sending payload nonblocking"
41
+ assert(s1.send_string(payload, ZMQ::NOBLOCK))
42
+ @unsent = false
43
+ end
44
+
45
+ # check for messages after 1 second
46
+ if Time.now - start_time > 1
47
+ poller.readables.each do |sock|
48
+ received_msg = ''
49
+ assert(sock.recv_string(received_msg, ZMQ::NOBLOCK))
50
+
51
+ puts "message received [#{received_msg}]"
52
+ @done = true
53
+ end
54
+ end
55
+ end
56
+
57
+ puts "executed in [#{Time.now - start_time}] seconds"
58
+
59
+ assert(s1.close)
60
+ assert(s2.close)
61
+
62
+ ctx.terminate
@@ -0,0 +1,40 @@
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
+ begin
12
+ ctx = ZMQ::Context.new
13
+ s1 = ctx.socket(ZMQ::REQ)
14
+ s2 = ctx.socket(ZMQ::REP)
15
+ rescue ContextError => e
16
+ STDERR.puts "Failed to allocate context or socket"
17
+ raise
18
+ end
19
+
20
+ assert(s1.setsockopt(ZMQ::LINGER, 100))
21
+ assert(s2.setsockopt(ZMQ::LINGER, 100))
22
+
23
+ assert(s2.bind(link))
24
+ assert(s1.connect(link))
25
+
26
+ payload = "#{ '3' * 2048 }"
27
+ sent_msg = ZMQ::Message.new(payload)
28
+ received_msg = ZMQ::Message.new
29
+
30
+ assert(s1.send(sent_msg))
31
+ assert(s2.recv(received_msg))
32
+
33
+ result = payload == received_msg.copy_out_string ? "Request received" : "Received wrong payload"
34
+
35
+ p result
36
+
37
+ assert(s1.close)
38
+ assert(s2.close)
39
+
40
+ ctx.terminate
@@ -0,0 +1,138 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
2
+
3
+
4
+ # Within a single process, we start up five threads. Main thread has a PUB (publisher)
5
+ # socket and the secondary threads have SUB (subscription) sockets. We measure the
6
+ # *throughput* between these sockets. A high-water mark (HWM) is *not* set, so the
7
+ # publisher queue is free to grow to the size of memory without dropping packets.
8
+ #
9
+ # This example also illustrates how a single context can be shared amongst several
10
+ # threads. Sharing a single context also allows a user to specify the "inproc"
11
+ # transport in addition to "tcp" and "ipc".
12
+ #
13
+ # % ruby throughput_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
14
+ #
15
+ # % ruby throughput_measurement.rb inproc://lm_sock 1024 1_000_000
16
+ #
17
+
18
+ if ARGV.length < 3
19
+ puts "usage: ruby throughput_measurement.rb <connect-to> <message-size> <roundtrip-count>"
20
+ exit
21
+ end
22
+
23
+ link = ARGV[0]
24
+ message_size = ARGV[1].to_i
25
+ count = ARGV[2].to_i
26
+
27
+ def assert(rc)
28
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
29
+ end
30
+
31
+ begin
32
+ master_context = ZMQ::Context.new
33
+ rescue ContextError => e
34
+ STDERR.puts "Failed to allocate context or socket!"
35
+ raise
36
+ end
37
+
38
+
39
+ class Receiver
40
+ def initialize context, link, size, count
41
+ @context = context
42
+ @link = link
43
+ @size = size
44
+ @count = count
45
+
46
+ begin
47
+ @socket = @context.socket(ZMQ::SUB)
48
+ rescue ContextError => e
49
+ STDERR.puts "Failed to allocate SUB socket!"
50
+ raise
51
+ end
52
+
53
+ assert(@socket.setsockopt(ZMQ::LINGER, 100))
54
+ assert(@socket.setsockopt(ZMQ::SUBSCRIBE, ""))
55
+
56
+ assert(@socket.connect(@link))
57
+ end
58
+
59
+ def run
60
+ msg = ZMQ::Message.new
61
+ assert(@socket.recv(msg))
62
+
63
+ elapsed = elapsed_microseconds do
64
+ (@count -1).times do
65
+ assert(@socket.recv(msg))
66
+ end
67
+ end
68
+
69
+ throughput = @count * 1000000 / elapsed
70
+ megabits = throughput * @size * 8 / 1000000
71
+
72
+ puts "message size: %i [B]" % @size
73
+ puts "message count: %i" % @count
74
+ puts "mean throughput: %i [msg/s]" % throughput
75
+ puts "mean throughput: %.3f [Mb/s]" % megabits
76
+
77
+ assert(@socket.close)
78
+ end
79
+
80
+ def elapsed_microseconds(&blk)
81
+ start = Time.now
82
+ yield
83
+ ((Time.now - start) * 1_000_000)
84
+ end
85
+ end
86
+
87
+ class Transmitter
88
+ def initialize context, link, size, count
89
+ @context = context
90
+ @link = link
91
+ @size = size
92
+ @count = count
93
+
94
+ begin
95
+ @socket = @context.socket(ZMQ::PUB)
96
+ rescue ContextError => e
97
+ STDERR.puts "Failed to allocate PUB socket!"
98
+ raise
99
+ end
100
+
101
+ assert(@socket.setsockopt(ZMQ::LINGER, 100))
102
+
103
+ assert(@socket.bind(@link))
104
+ end
105
+
106
+ def run
107
+ sleep 1
108
+ contents = "#{'0' * @size}"
109
+
110
+ i = 0
111
+ while i < @count
112
+ msg = ZMQ::Message.new(contents)
113
+ assert(@socket.send(msg))
114
+ i += 1
115
+ end
116
+
117
+ assert(@socket.close)
118
+ end
119
+ end
120
+
121
+ threads = []
122
+
123
+ threads << Thread.new do
124
+ transmitter = Transmitter.new(master_context, link, message_size, count)
125
+ transmitter.run
126
+ end
127
+
128
+ 1.times do
129
+ threads << Thread.new do
130
+ receiver = Receiver.new(master_context, link, message_size, count)
131
+ receiver.run
132
+ end
133
+ end
134
+
135
+
136
+ threads.each {|t| t.join}
137
+
138
+ master_context.terminate