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
@@ -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