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
@@ -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
|
@@ -0,0 +1,69 @@
|
|
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::SUBSCRIBE, ""))
|
29
|
+
assert(s.setsockopt(ZMQ::RCVHWM, 20))
|
30
|
+
#assert(s.setsockopt(ZMQ::RCVHWM, 0))
|
31
|
+
#assert(s.setsockopt(ZMQ::SNDHWM, 0))
|
32
|
+
|
33
|
+
assert(s.connect(bind_to))
|
34
|
+
sleep 1
|
35
|
+
|
36
|
+
msg = ZMQ::Message.new
|
37
|
+
msg = ''
|
38
|
+
assert(s.recv_string(msg))
|
39
|
+
raise unless msg.to_i == 0
|
40
|
+
|
41
|
+
start_time = Time.now
|
42
|
+
|
43
|
+
i = 1
|
44
|
+
while i < message_count
|
45
|
+
assert(s.recv_string(msg))
|
46
|
+
msg_i = msg.to_i
|
47
|
+
puts "missed [#{msg_i - i}] messages"
|
48
|
+
i = msg_i
|
49
|
+
sleep(sleep_time)
|
50
|
+
end
|
51
|
+
|
52
|
+
end_time = Time.now
|
53
|
+
|
54
|
+
elapsed = (end_time.to_f - start_time.to_f) * 1000000
|
55
|
+
if elapsed == 0
|
56
|
+
elapsed = 1
|
57
|
+
end
|
58
|
+
|
59
|
+
throughput = message_count * 1000000 / elapsed
|
60
|
+
megabits = throughput * message_size * 8 / 1000000
|
61
|
+
|
62
|
+
puts "message size: %i [B]" % message_size
|
63
|
+
puts "message count: %i" % message_count
|
64
|
+
puts "mean throughput: %i [msg/s]" % throughput
|
65
|
+
puts "mean throughput: %.3f [Mb/s]" % megabits
|
66
|
+
|
67
|
+
assert(s.close)
|
68
|
+
|
69
|
+
ctx.terminate
|
@@ -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}"
|
data/ffi-rzmq.gemspec
CHANGED
@@ -6,12 +6,12 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "ffi-rzmq"
|
7
7
|
s.version = ZMQ::VERSION
|
8
8
|
s.authors = ["Chuck Remes"]
|
9
|
-
s.email = ["
|
9
|
+
s.email = ["git@chuckremes.com"]
|
10
10
|
s.homepage = "http://github.com/chuckremes/ffi-rzmq"
|
11
11
|
s.summary = %q{This gem wraps the ZeroMQ (0mq) networking library using Ruby FFI (foreign function interface).}
|
12
12
|
s.description = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign
|
13
13
|
function interface). It's a pure ruby wrapper so this gem can be loaded
|
14
|
-
and run by any ruby runtime that supports FFI. That's all of them
|
14
|
+
and run by any ruby runtime that supports FFI. That's all of them -
|
15
15
|
MRI 1.9.x, Rubinius and JRuby.}
|
16
16
|
|
17
17
|
s.rubyforge_project = "ffi-rzmq"
|
data/lib/ffi-rzmq/constants.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module ZMQ
|
2
2
|
# Set up all of the constants that are *common* to all API
|
3
3
|
# versions
|
4
|
-
|
4
|
+
|
5
5
|
# Socket types
|
6
6
|
PAIR = 0
|
7
7
|
PUB = 1
|
@@ -41,6 +41,8 @@ module ZMQ
|
|
41
41
|
RECONNECT_IVL = 18
|
42
42
|
BACKLOG = 19
|
43
43
|
RECONNECT_IVL_MAX = 21
|
44
|
+
RCVTIMEO = 27
|
45
|
+
SNDTIMEO = 28
|
44
46
|
|
45
47
|
# Send/recv options
|
46
48
|
SNDMORE = 2
|
@@ -58,13 +60,14 @@ module ZMQ
|
|
58
60
|
ENOMEM = Errno::ENOMEM::Errno
|
59
61
|
ENODEV = Errno::ENODEV::Errno
|
60
62
|
EFAULT = Errno::EFAULT::Errno
|
63
|
+
EINTR = Errno::EINTR::Errno
|
61
64
|
|
62
65
|
# ZMQ errors
|
63
66
|
HAUSNUMERO = 156384712
|
64
|
-
EMTHREAD = (HAUSNUMERO + 50)
|
65
67
|
EFSM = (HAUSNUMERO + 51)
|
66
68
|
ENOCOMPATPROTO = (HAUSNUMERO + 52)
|
67
69
|
ETERM = (HAUSNUMERO + 53)
|
70
|
+
EMTHREAD = (HAUSNUMERO + 54)
|
68
71
|
|
69
72
|
# Rescue unknown constants and use the ZeroMQ defined values
|
70
73
|
# Usually only happens on Windows though some don't resolve on
|
@@ -78,7 +81,20 @@ module ZMQ
|
|
78
81
|
ECONNREFUSED = Errno::ECONNREFUSED::Errno rescue (HAUSNUMERO + 7)
|
79
82
|
EINPROGRESS = Errno::EINPROGRESS::Errno rescue (HAUSNUMERO + 8)
|
80
83
|
ENOTSOCK = Errno::ENOTSOCK::Errno rescue (HAUSNUMERO + 9)
|
81
|
-
|
84
|
+
EMSGSIZE = Errno::EMSGSIZE::Errno rescue (HAUSNUMERO + 10)
|
85
|
+
EAFNOSUPPORT = Errno::EAFNOSUPPORT::Errno rescue (HAUSNUMERO + 11)
|
86
|
+
ENETUNREACH = Errno::ENETUNREACH::Errno rescue (HAUSNUMERO + 12)
|
87
|
+
ECONNABORTED = Errno::ECONNABORTED::Errno rescue (HAUSNUMERO + 13)
|
88
|
+
ECONNRESET = Errno::ECONNRESET::Errno rescue (HAUSNUMERO + 14)
|
89
|
+
ENOTCONN = Errno::ENOTCONN::Errno rescue (HAUSNUMERO + 15)
|
90
|
+
ETIMEDOUT = Errno::ETIMEDOUT::Errno rescue (HAUSNUMERO + 16)
|
91
|
+
EHOSTUNREACH = Errno::EHOSTUNREACH::Errno rescue (HAUSNUMERO + 17)
|
92
|
+
ENETRESET = Errno::ENETRESET::Errno rescue (HAUSNUMERO + 18)
|
93
|
+
|
94
|
+
# Device Types
|
95
|
+
STREAMER = 1
|
96
|
+
FORWARDER = 2
|
97
|
+
QUEUE = 3
|
82
98
|
end # module ZMQ
|
83
99
|
|
84
100
|
|
@@ -93,11 +109,6 @@ if ZMQ::LibZMQ.version2?
|
|
93
109
|
SocketTypeNameMap[ROUTER] = 'ROUTER'
|
94
110
|
SocketTypeNameMap[DEALER] = 'DEALER'
|
95
111
|
|
96
|
-
# Device Types
|
97
|
-
STREAMER = 1
|
98
|
-
FORWARDER = 2
|
99
|
-
QUEUE = 3
|
100
|
-
|
101
112
|
# Socket options
|
102
113
|
HWM = 1
|
103
114
|
IDENTITY = 5
|
@@ -107,6 +118,7 @@ if ZMQ::LibZMQ.version2?
|
|
107
118
|
|
108
119
|
# Send/recv options
|
109
120
|
NOBLOCK = 1
|
121
|
+
NonBlocking = NOBLOCK
|
110
122
|
end
|
111
123
|
end # version2?
|
112
124
|
|
@@ -124,22 +136,48 @@ if ZMQ::LibZMQ.version3?
|
|
124
136
|
SocketTypeNameMap[XPUB] = 'XPUB'
|
125
137
|
SocketTypeNameMap[XSUB] = 'XSUB'
|
126
138
|
|
139
|
+
# Context options
|
140
|
+
IO_THREADS = 1
|
141
|
+
MAX_SOCKETS = 2
|
142
|
+
IO_THREADS_DFLT = 1
|
143
|
+
MAX_SOCKETS_DFLT = 1024
|
144
|
+
|
127
145
|
# Socket options
|
128
|
-
IDENTITY
|
129
|
-
MAXMSGSIZE
|
130
|
-
SNDHWM
|
131
|
-
RCVHWM
|
146
|
+
IDENTITY = 5
|
147
|
+
MAXMSGSIZE = 22
|
148
|
+
SNDHWM = 23
|
149
|
+
RCVHWM = 24
|
132
150
|
MULTICAST_HOPS = 25
|
133
|
-
|
134
|
-
|
151
|
+
IPV4ONLY = 31
|
152
|
+
LAST_ENDPOINT = 32
|
153
|
+
ROUTER_BEHAVIOR = 33
|
154
|
+
TCP_KEEPALIVE = 34
|
155
|
+
TCP_KEEPALIVE_CNT = 35
|
156
|
+
TCP_KEEPALIVE_IDLE = 36
|
157
|
+
TCP_KEEPALIVE_INTVL = 37
|
158
|
+
TCP_ACCEPT_FILTER = 38
|
159
|
+
|
160
|
+
# Message options
|
161
|
+
MORE = 1
|
135
162
|
|
136
163
|
# Send/recv options
|
137
|
-
DONTWAIT
|
138
|
-
SNDLABEL
|
139
|
-
|
164
|
+
DONTWAIT = 1
|
165
|
+
SNDLABEL = 4
|
166
|
+
NonBlocking = DONTWAIT
|
167
|
+
|
168
|
+
# Socket events and monitoring
|
169
|
+
EVENT_CONNECTED = 1
|
170
|
+
EVENT_CONNECT_DELAYED = 2
|
171
|
+
EVENT_CONNECT_RETRIED = 4
|
172
|
+
EVENT_LISTENING = 8
|
173
|
+
EVENT_BIND_FAILED = 16
|
174
|
+
EVENT_ACCEPTED = 32
|
175
|
+
EVENT_ACCEPT_FAILED = 64
|
176
|
+
EVENT_CLOSED = 128
|
177
|
+
EVENT_CLOSE_FAILED = 256
|
178
|
+
EVENT_DISCONNECTED = 512
|
140
179
|
|
141
180
|
# Socket & other errors
|
142
181
|
EMFILE = Errno::EMFILE::Errno
|
143
|
-
|
144
182
|
end
|
145
183
|
end # version3?
|
data/lib/ffi-rzmq/context.rb
CHANGED
@@ -3,9 +3,9 @@ module ZMQ
|
|
3
3
|
|
4
4
|
|
5
5
|
# Recommended to use the default for +io_threads+
|
6
|
-
# since most programs will not saturate I/O.
|
6
|
+
# since most programs will not saturate I/O.
|
7
7
|
#
|
8
|
-
# The rule of thumb is to make +io_threads+ equal to the number
|
8
|
+
# The rule of thumb is to make +io_threads+ equal to the number
|
9
9
|
# gigabits per second that the application will produce.
|
10
10
|
#
|
11
11
|
# The +io_threads+ number specifies the size of the thread pool
|
@@ -41,23 +41,50 @@ module ZMQ
|
|
41
41
|
#
|
42
42
|
#
|
43
43
|
class Context
|
44
|
-
include ZMQ::Util
|
45
44
|
|
46
|
-
attr_reader :context, :
|
45
|
+
attr_reader :context, :io_threads, :max_sockets
|
46
|
+
alias :pointer :context
|
47
47
|
|
48
|
-
def self.create io_threads = 1
|
49
|
-
new(io_threads) rescue nil
|
50
|
-
end
|
51
|
-
|
52
48
|
# Use the factory method Context#create to make contexts.
|
53
49
|
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
if LibZMQ.version2?
|
51
|
+
def self.create io_threads = 1
|
52
|
+
new(io_threads) rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize io_threads = 1
|
56
|
+
@io_threads = io_threads
|
57
|
+
@context = LibZMQ.zmq_init io_threads
|
58
|
+
ZMQ::Util.error_check 'zmq_init', (@context.nil? || @context.null?) ? -1 : 0
|
59
|
+
|
60
|
+
define_finalizer
|
61
|
+
end
|
62
|
+
elsif LibZMQ.version3?
|
63
|
+
|
64
|
+
def self.create(opts = {})
|
65
|
+
new(opts) rescue nil
|
66
|
+
end
|
59
67
|
|
60
|
-
|
68
|
+
def initialize(opts = {})
|
69
|
+
if opts.respond_to?(:empty?)
|
70
|
+
@io_threads = opts[:io_threads] || IO_THREADS_DFLT
|
71
|
+
@max_sockets = opts[:max_sockets] || MAX_SOCKETS_DFLT
|
72
|
+
else
|
73
|
+
@io_threads = opts || 1
|
74
|
+
@max_sockets = MAX_SOCKETS_DFLT
|
75
|
+
end
|
76
|
+
|
77
|
+
@context = LibZMQ.zmq_ctx_new
|
78
|
+
ZMQ::Util.error_check 'zmq_ctx_new', (@context.nil? || @context.null?) ? -1 : 0
|
79
|
+
|
80
|
+
rc = LibZMQ.zmq_ctx_set(@context, ZMQ::IO_THREADS, @io_threads)
|
81
|
+
ZMQ::Util.error_check 'zmq_ctx_set', rc
|
82
|
+
|
83
|
+
rc = LibZMQ.zmq_ctx_set(@context, ZMQ::MAX_SOCKETS, @max_sockets)
|
84
|
+
ZMQ::Util.error_check 'zmq_ctx_set', rc
|
85
|
+
|
86
|
+
define_finalizer
|
87
|
+
end
|
61
88
|
end
|
62
89
|
|
63
90
|
# Call to release the context and any remaining data associated
|
@@ -67,15 +94,27 @@ module ZMQ
|
|
67
94
|
#
|
68
95
|
# Returns 0 for success, -1 for failure.
|
69
96
|
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
97
|
+
if LibZMQ.version2?
|
98
|
+
def terminate
|
99
|
+
unless @context.nil? || @context.null?
|
100
|
+
remove_finalizer
|
101
|
+
rc = LibZMQ.zmq_term @context
|
102
|
+
@context = nil
|
103
|
+
rc
|
104
|
+
else
|
105
|
+
0
|
106
|
+
end
|
107
|
+
end
|
108
|
+
elsif LibZMQ.version3?
|
109
|
+
def terminate
|
110
|
+
unless @context.nil? || @context.null?
|
111
|
+
remove_finalizer
|
112
|
+
rc = LibZMQ.zmq_ctx_destroy(@context)
|
113
|
+
@context = nil
|
114
|
+
rc
|
115
|
+
else
|
116
|
+
0
|
117
|
+
end
|
79
118
|
end
|
80
119
|
end
|
81
120
|
|
@@ -102,7 +141,7 @@ module ZMQ
|
|
102
141
|
rescue ContextError => e
|
103
142
|
sock = nil
|
104
143
|
end
|
105
|
-
|
144
|
+
|
106
145
|
sock
|
107
146
|
end
|
108
147
|
|
data/lib/ffi-rzmq/device.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
module ZMQ
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
|
3
|
+
class Device
|
4
|
+
attr_reader :device
|
5
|
+
|
6
|
+
def self.create(device_type, frontend, backend)
|
7
|
+
dev = nil
|
8
|
+
begin
|
9
|
+
dev = new(device_type, frontend, backend)
|
10
|
+
rescue ArgumentError
|
7
11
|
dev = nil
|
8
|
-
begin
|
9
|
-
dev = new device_type, frontend, backend
|
10
|
-
rescue ArgumentError
|
11
|
-
dev = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
dev
|
15
12
|
end
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
unless socket.is_a?(ZMQ::Socket)
|
20
|
-
raise ArgumentError, "Expected a ZMQ::Socket, not a #{socket.class} as the #{name}"
|
21
|
-
end
|
22
|
-
end
|
14
|
+
dev
|
15
|
+
end
|
23
16
|
|
24
|
-
|
17
|
+
def initialize(device_type, frontend, backend)
|
18
|
+
[["frontend", frontend], ["backend", backend]].each do |name, socket|
|
19
|
+
unless socket.is_a?(ZMQ::Socket)
|
20
|
+
raise ArgumentError, "Expected a ZMQ::Socket, not a #{socket.class} as the #{name}"
|
21
|
+
end
|
25
22
|
end
|
23
|
+
|
24
|
+
LibZMQ.zmq_device(device_type, frontend.socket, backend.socket)
|
26
25
|
end
|
27
26
|
end
|
27
|
+
|
28
28
|
end
|