ffi-rzmq 0.9.3 → 0.9.6
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/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
|