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.
- data/AUTHORS.txt +1 -0
- data/History.txt +35 -0
- data/README.rdoc +48 -15
- data/Rakefile +7 -2
- data/examples/README.rdoc +21 -76
- data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
- data/examples/v2api/local_lat_poll.rb +66 -0
- data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
- data/examples/v2api/publish_subscribe.rb +82 -0
- data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
- data/examples/v2api/remote_throughput.rb +39 -0
- data/examples/v2api/reqrep_poll.rb +62 -0
- data/examples/v2api/request_response.rb +40 -0
- data/examples/v2api/throughput_measurement.rb +138 -0
- data/examples/v3api/local_lat.rb +59 -0
- data/examples/v3api/local_lat_poll.rb +66 -0
- data/examples/v3api/local_throughput.rb +65 -0
- data/examples/v3api/publish_subscribe.rb +82 -0
- data/examples/v3api/remote_lat.rb +71 -0
- data/examples/v3api/remote_throughput.rb +47 -0
- data/examples/v3api/reqrep_poll.rb +62 -0
- data/examples/v3api/request_response.rb +40 -0
- data/examples/v3api/throughput_measurement.rb +166 -0
- data/ext/README +5 -0
- data/ffi-rzmq.gemspec +4 -4
- data/lib/ffi-rzmq.rb +4 -1
- data/lib/ffi-rzmq/constants.rb +178 -0
- data/lib/ffi-rzmq/context.rb +61 -45
- data/lib/ffi-rzmq/device.rb +22 -9
- data/lib/ffi-rzmq/exceptions.rb +0 -98
- data/lib/ffi-rzmq/libc.rb +19 -0
- data/lib/ffi-rzmq/libzmq.rb +188 -0
- data/lib/ffi-rzmq/message.rb +33 -40
- data/lib/ffi-rzmq/poll.rb +49 -52
- data/lib/ffi-rzmq/socket.rb +902 -392
- data/lib/ffi-rzmq/util.rb +101 -0
- data/spec/context_spec.rb +47 -21
- data/spec/device_spec.rb +78 -58
- data/spec/message_spec.rb +90 -12
- data/spec/multipart_spec.rb +162 -0
- data/spec/nonblocking_recv_spec.rb +325 -0
- data/spec/pushpull_spec.rb +95 -34
- data/spec/reqrep_spec.rb +55 -20
- data/spec/socket_spec.rb +353 -204
- data/spec/spec_helper.rb +46 -3
- data/version.txt +1 -1
- metadata +91 -66
- data/examples/local_lat_poll.rb +0 -54
- data/examples/local_lat_zerocopy.rb +0 -24
- data/examples/publish_subscribe.rb +0 -52
- data/examples/remote_lat_zerocopy.rb +0 -35
- data/examples/remote_throughput.rb +0 -27
- data/examples/reqrep_poll.rb +0 -49
- data/examples/request_response.rb +0 -23
- data/lib/ffi-rzmq/wrapper.rb +0 -121
- data/lib/ffi-rzmq/zmq.rb +0 -198
@@ -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.sendmsg(sent_msg))
|
31
|
+
assert(s2.recvmsg(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,166 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ffi-rzmq')
|
2
|
+
require 'thread'
|
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, stats
|
41
|
+
@context = context
|
42
|
+
@link = link
|
43
|
+
@size = size
|
44
|
+
@count = count
|
45
|
+
@stats = stats
|
46
|
+
|
47
|
+
begin
|
48
|
+
@socket = @context.socket(ZMQ::SUB)
|
49
|
+
rescue ContextError => e
|
50
|
+
STDERR.puts "Failed to allocate SUB socket!"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
55
|
+
assert(@socket.setsockopt(ZMQ::SUBSCRIBE, ""))
|
56
|
+
|
57
|
+
assert(@socket.connect(@link))
|
58
|
+
end
|
59
|
+
|
60
|
+
def run
|
61
|
+
msg = ZMQ::Message.new
|
62
|
+
assert(@socket.recvmsg(msg))
|
63
|
+
|
64
|
+
elapsed = elapsed_microseconds do
|
65
|
+
(@count - 1).times do
|
66
|
+
assert(@socket.recvmsg(msg))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@stats.record_elapsed(elapsed)
|
71
|
+
assert(@socket.close)
|
72
|
+
end
|
73
|
+
|
74
|
+
def elapsed_microseconds(&blk)
|
75
|
+
start = Time.now
|
76
|
+
yield
|
77
|
+
((Time.now - start) * 1_000_000)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Transmitter
|
82
|
+
def initialize context, link, size, count
|
83
|
+
@context = context
|
84
|
+
@link = link
|
85
|
+
@size = size
|
86
|
+
@count = count
|
87
|
+
|
88
|
+
begin
|
89
|
+
@socket = @context.socket(ZMQ::PUB)
|
90
|
+
rescue ContextError => e
|
91
|
+
STDERR.puts "Failed to allocate PUB socket!"
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
|
95
|
+
assert(@socket.setsockopt(ZMQ::LINGER, 100))
|
96
|
+
assert(@socket.bind(@link))
|
97
|
+
end
|
98
|
+
|
99
|
+
def run
|
100
|
+
sleep 1
|
101
|
+
contents = "#{'0' * @size}"
|
102
|
+
|
103
|
+
i = 0
|
104
|
+
while i < @count
|
105
|
+
msg = ZMQ::Message.new(contents)
|
106
|
+
assert(@socket.sendmsg(msg))
|
107
|
+
i += 1
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
def close
|
113
|
+
assert(@socket.close)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Stats
|
118
|
+
def initialize size, count
|
119
|
+
@size = size
|
120
|
+
@count = count
|
121
|
+
|
122
|
+
@mutex = Mutex.new
|
123
|
+
@elapsed = []
|
124
|
+
end
|
125
|
+
|
126
|
+
def record_elapsed(elapsed)
|
127
|
+
@mutex.synchronize do
|
128
|
+
@elapsed << elapsed
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def output
|
133
|
+
@elapsed.each do |elapsed|
|
134
|
+
throughput = @count * 1000000 / elapsed
|
135
|
+
megabits = throughput * @size * 8 / 1000000
|
136
|
+
|
137
|
+
puts "message size: %i [B]" % @size
|
138
|
+
puts "message count: %i" % @count
|
139
|
+
puts "mean throughput: %i [msg/s]" % throughput
|
140
|
+
puts "mean throughput: %.3f [Mb/s]" % megabits
|
141
|
+
puts
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
threads = []
|
147
|
+
stats = Stats.new message_size, count
|
148
|
+
transmitter = Transmitter.new(master_context, link, message_size, count)
|
149
|
+
|
150
|
+
threads << Thread.new do
|
151
|
+
transmitter.run
|
152
|
+
end
|
153
|
+
|
154
|
+
1.times do
|
155
|
+
threads << Thread.new do
|
156
|
+
receiver = Receiver.new(master_context, link, message_size, count, stats)
|
157
|
+
receiver.run
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
threads.each {|t| t.join}
|
163
|
+
transmitter.close
|
164
|
+
stats.output
|
165
|
+
|
166
|
+
master_context.terminate
|
data/ext/README
ADDED
data/ffi-rzmq.gemspec
CHANGED
@@ -2,24 +2,24 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{ffi-rzmq}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.9.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Chuck Remes"]
|
9
|
-
s.date = %q{2011-
|
9
|
+
s.date = %q{2011-09-14}
|
10
10
|
s.description = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign
|
11
11
|
function interface). It's a pure ruby wrapper so this gem can be loaded
|
12
12
|
and run by any ruby runtime that supports FFI. That's all of them:
|
13
13
|
MRI 1.9.x, Rubinius and JRuby.}
|
14
14
|
s.email = %q{cremes@mac.com}
|
15
15
|
s.extra_rdoc_files = ["AUTHORS.txt", "History.txt", "README.rdoc", "examples/README.rdoc", "version.txt"]
|
16
|
-
s.files = [".bnsignore", "History.txt", "README.rdoc", "Rakefile", "examples/README.rdoc", "examples/local_lat.rb", "examples/local_lat_poll.rb", "examples/
|
16
|
+
s.files = [".bnsignore", "History.txt", "README.rdoc", "Rakefile", "examples/README.rdoc", "examples/v2api/local_lat.rb", "examples/v2api/local_lat_poll.rb", "examples/v2api/local_throughput.rb", "examples/v2api/publish_subscribe.rb", "examples/v2api/remote_lat.rb", "examples/v2api/remote_throughput.rb", "examples/v2api/reqrep_poll.rb", "examples/v2api/request_response.rb", "examples/v2api/throughput_measurement.rb", "examples/v3api/local_lat.rb", "examples/v3api/local_lat_poll.rb", "examples/v3api/local_throughput.rb", "examples/v3api/publish_subscribe.rb", "examples/v3api/remote_lat.rb", "examples/v3api/remote_throughput.rb", "examples/v3api/reqrep_poll.rb", "examples/v3api/request_response.rb", "examples/v3api/throughput_measurement.rb", "ext/README", "ffi-rzmq.gemspec", "lib/ffi-rzmq.rb", "lib/ffi-rzmq/constants.rb", "lib/ffi-rzmq/context.rb", "lib/ffi-rzmq/device.rb", "lib/ffi-rzmq/exceptions.rb", "lib/ffi-rzmq/libc.rb", "lib/ffi-rzmq/libzmq.rb", "lib/ffi-rzmq/message.rb", "lib/ffi-rzmq/poll.rb", "lib/ffi-rzmq/poll_items.rb", "lib/ffi-rzmq/socket.rb", "lib/ffi-rzmq/util.rb", "spec/context_spec.rb", "spec/device_spec.rb", "spec/message_spec.rb", "spec/multipart_spec.rb", "spec/nonblocking_recv_spec.rb", "spec/pushpull_spec.rb", "spec/reqrep_spec.rb", "spec/socket_spec.rb", "spec/spec_helper.rb", "version.txt"]
|
17
17
|
s.homepage = %q{http://github.com/chuckremes/ffi-rzmq}
|
18
18
|
s.rdoc_options = ["--main", "README.rdoc"]
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
s.rubyforge_project = %q{ffi-rzmq}
|
21
21
|
s.rubygems_version = %q{1.3.7}
|
22
|
-
s.summary = %q{This gem wraps the ZeroMQ networking library using
|
22
|
+
s.summary = %q{This gem wraps the ZeroMQ (0mq) networking library using Ruby FFI (foreign function interface).}
|
23
23
|
|
24
24
|
if s.respond_to? :specification_version then
|
25
25
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
data/lib/ffi-rzmq.rb
CHANGED
@@ -66,7 +66,10 @@ end # module ZMQ
|
|
66
66
|
|
67
67
|
RBX = defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/ ? true : false
|
68
68
|
|
69
|
+
require 'ffi' unless RBX
|
70
|
+
|
69
71
|
# the order of files is important
|
70
|
-
|
72
|
+
#%w(wrapper zmq exceptions context message socket poll_items poll device).each do |file|
|
73
|
+
%w(libc libzmq constants util exceptions context message socket poll_items poll device).each do |file|
|
71
74
|
require ZMQ.libpath(['ffi-rzmq', file])
|
72
75
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module ZMQ
|
2
|
+
# Set up all of the constants that are *common* to all API
|
3
|
+
# versions
|
4
|
+
|
5
|
+
# Socket types
|
6
|
+
PAIR = 0
|
7
|
+
PUB = 1
|
8
|
+
SUB = 2
|
9
|
+
REQ = 3
|
10
|
+
REP = 4
|
11
|
+
XREQ = 5
|
12
|
+
XREP = 6
|
13
|
+
PULL = 7
|
14
|
+
PUSH = 8
|
15
|
+
|
16
|
+
SocketTypeNameMap = {
|
17
|
+
PAIR => "PAIR",
|
18
|
+
PUB => "PUB",
|
19
|
+
SUB => "SUB",
|
20
|
+
REQ => "REQ",
|
21
|
+
REP => "REP",
|
22
|
+
PULL => "PULL",
|
23
|
+
PUSH => "PUSH",
|
24
|
+
XREQ => "XREQ",
|
25
|
+
XREP => "XREP"
|
26
|
+
}
|
27
|
+
|
28
|
+
# Socket options
|
29
|
+
AFFINITY = 4
|
30
|
+
SUBSCRIBE = 6
|
31
|
+
UNSUBSCRIBE = 7
|
32
|
+
RATE = 8
|
33
|
+
RECOVERY_IVL = 9
|
34
|
+
SNDBUF = 11
|
35
|
+
RCVBUF = 12
|
36
|
+
RCVMORE = 13
|
37
|
+
FD = 14
|
38
|
+
EVENTS = 15
|
39
|
+
TYPE = 16
|
40
|
+
LINGER = 17
|
41
|
+
RECONNECT_IVL = 18
|
42
|
+
BACKLOG = 19
|
43
|
+
RECONNECT_IVL_MAX = 21
|
44
|
+
|
45
|
+
# Send/recv options
|
46
|
+
SNDMORE = 2
|
47
|
+
|
48
|
+
# I/O multiplexing
|
49
|
+
|
50
|
+
POLL = 1
|
51
|
+
POLLIN = 1
|
52
|
+
POLLOUT = 2
|
53
|
+
POLLERR = 4
|
54
|
+
|
55
|
+
# Socket errors
|
56
|
+
EAGAIN = Errno::EAGAIN::Errno
|
57
|
+
EINVAL = Errno::EINVAL::Errno
|
58
|
+
ENOMEM = Errno::ENOMEM::Errno
|
59
|
+
ENODEV = Errno::ENODEV::Errno
|
60
|
+
EFAULT = Errno::EFAULT::Errno
|
61
|
+
|
62
|
+
# ZMQ errors
|
63
|
+
HAUSNUMERO = 156384712
|
64
|
+
EMTHREAD = (HAUSNUMERO + 50)
|
65
|
+
EFSM = (HAUSNUMERO + 51)
|
66
|
+
ENOCOMPATPROTO = (HAUSNUMERO + 52)
|
67
|
+
ETERM = (HAUSNUMERO + 53)
|
68
|
+
|
69
|
+
# Rescue unknown constants and use the ZeroMQ defined values
|
70
|
+
# Usually only happens on Windows though some don't resolve on
|
71
|
+
# OSX too (ENOTSUP)
|
72
|
+
ENOTSUP = Errno::ENOTSUP::Errno rescue (HAUSNUMERO + 1)
|
73
|
+
EPROTONOSUPPORT = Errno::EPROTONOSUPPORT::Errno rescue (HAUSNUMERO + 2)
|
74
|
+
ENOBUFS = Errno::ENOBUFS::Errno rescue (HAUSNUMERO + 3)
|
75
|
+
ENETDOWN = Errno::ENETDOWN::Errno rescue (HAUSNUMERO + 4)
|
76
|
+
EADDRINUSE = Errno::EADDRINUSE::Errno rescue (HAUSNUMERO + 5)
|
77
|
+
EADDRNOTAVAIL = Errno::EADDRNOTAVAIL::Errno rescue (HAUSNUMERO + 6)
|
78
|
+
ECONNREFUSED = Errno::ECONNREFUSED::Errno rescue (HAUSNUMERO + 7)
|
79
|
+
EINPROGRESS = Errno::EINPROGRESS::Errno rescue (HAUSNUMERO + 8)
|
80
|
+
ENOTSOCK = Errno::ENOTSOCK::Errno rescue (HAUSNUMERO + 9)
|
81
|
+
EINTR = Errno::EINTR::Errno rescue (HAUSNUMERO + 10)
|
82
|
+
end # module ZMQ
|
83
|
+
|
84
|
+
|
85
|
+
if LibZMQ.version2?
|
86
|
+
module ZMQ
|
87
|
+
# Socket types
|
88
|
+
UPSTREAM = PULL
|
89
|
+
DOWNSTREAM = PUSH
|
90
|
+
DEALER = XREQ
|
91
|
+
ROUTER = XREP
|
92
|
+
|
93
|
+
SocketTypeNameMap[ROUTER] = 'ROUTER'
|
94
|
+
SocketTypeNameMap[DEALER] = 'DEALER'
|
95
|
+
|
96
|
+
# Device Types
|
97
|
+
STREAMER = 1
|
98
|
+
FORWARDER = 2
|
99
|
+
QUEUE = 3
|
100
|
+
|
101
|
+
# Socket options
|
102
|
+
HWM = 1
|
103
|
+
IDENTITY = 5
|
104
|
+
MCAST_LOOP = 10
|
105
|
+
SWAP = 3
|
106
|
+
RECOVERY_IVL_MSEC = 20
|
107
|
+
|
108
|
+
# Send/recv options
|
109
|
+
NOBLOCK = 1
|
110
|
+
end
|
111
|
+
end # version2?
|
112
|
+
|
113
|
+
|
114
|
+
if LibZMQ.version3?
|
115
|
+
module ZMQ
|
116
|
+
# Socket types
|
117
|
+
XPUB = 9
|
118
|
+
XSUB = 10
|
119
|
+
ROUTER = 11
|
120
|
+
DEALER = 12
|
121
|
+
|
122
|
+
SocketTypeNameMap[ROUTER] = 'ROUTER'
|
123
|
+
SocketTypeNameMap[DEALER] = 'DEALER'
|
124
|
+
SocketTypeNameMap[XPUB] = 'XPUB'
|
125
|
+
SocketTypeNameMap[XSUB] = 'XSUB'
|
126
|
+
|
127
|
+
# Socket options
|
128
|
+
IDENTITY = 5
|
129
|
+
MAXMSGSIZE = 22
|
130
|
+
SNDHWM = 23
|
131
|
+
RCVHWM = 24
|
132
|
+
MULTICAST_HOPS = 25
|
133
|
+
RCVTIMEO = 27
|
134
|
+
SNDTIMEO = 28
|
135
|
+
RCVLABEL = 29
|
136
|
+
|
137
|
+
# Send/recv options
|
138
|
+
DONTWAIT = 1
|
139
|
+
SNDLABEL = 4
|
140
|
+
|
141
|
+
|
142
|
+
# Socket & other errors
|
143
|
+
EMFILE = Errno::EMFILE::Errno
|
144
|
+
|
145
|
+
end
|
146
|
+
end # version3?
|
147
|
+
|
148
|
+
|
149
|
+
if LibZMQ.version4?
|
150
|
+
module ZMQ
|
151
|
+
# Socket types
|
152
|
+
XPUB = 9
|
153
|
+
XSUB = 10
|
154
|
+
ROUTER = 13
|
155
|
+
|
156
|
+
SocketTypeNameMap[ROUTER] = 'ROUTER'
|
157
|
+
SocketTypeNameMap[XPUB] = 'XPUB'
|
158
|
+
SocketTypeNameMap[XSUB] = 'XSUB'
|
159
|
+
|
160
|
+
# Socket options
|
161
|
+
MAXMSGSIZE = 22
|
162
|
+
SNDHWM = 23
|
163
|
+
RCVHWM = 24
|
164
|
+
MULTICAST_HOPS = 25
|
165
|
+
RCVTIMEO = 27
|
166
|
+
SNDTIMEO = 28
|
167
|
+
RCVLABEL = 29
|
168
|
+
|
169
|
+
# Send/recv options
|
170
|
+
DONTWAIT = 1
|
171
|
+
SNDLABEL = 4
|
172
|
+
|
173
|
+
|
174
|
+
# Socket & other errors
|
175
|
+
EMFILE = Errno::EMFILE::Errno
|
176
|
+
|
177
|
+
end
|
178
|
+
end # version4?
|
data/lib/ffi-rzmq/context.rb
CHANGED
@@ -1,70 +1,82 @@
|
|
1
1
|
|
2
2
|
module ZMQ
|
3
3
|
|
4
|
-
ZMQ_INIT_STR = 'zmq_init'.freeze
|
5
|
-
ZMQ_TERM_STR = 'zmq_term'.freeze
|
6
|
-
ZMQ_SOCKET_STR = 'zmq_socket'.freeze unless defined? ZMQ_SOCKET_STR
|
7
|
-
|
8
4
|
|
5
|
+
# Recommended to use the default for +io_threads+
|
6
|
+
# since most programs will not saturate I/O.
|
7
|
+
#
|
8
|
+
# The rule of thumb is to make +io_threads+ equal to the number
|
9
|
+
# gigabits per second that the application will produce.
|
10
|
+
#
|
11
|
+
# The +io_threads+ number specifies the size of the thread pool
|
12
|
+
# allocated by 0mq for processing incoming/outgoing messages.
|
13
|
+
#
|
14
|
+
# Returns a context object when allocation succeeds. It's necessary
|
15
|
+
# for passing to the
|
16
|
+
# #Socket constructor when allocating new sockets. All sockets
|
17
|
+
# live within a context.
|
18
|
+
#
|
19
|
+
# Also, Sockets should *only* be accessed from the thread where they
|
20
|
+
# were first created. Do *not* pass sockets between threads; pass
|
21
|
+
# in the context and allocate a new socket per thread. If you must
|
22
|
+
# use threads, then make sure to execute a full memory barrier (e.g.
|
23
|
+
# mutex) as you pass a socket from one thread to the next.
|
24
|
+
#
|
25
|
+
# To connect sockets between contexts, use +inproc+ or +ipc+
|
26
|
+
# transport and set up a 0mq socket between them. This is also the
|
27
|
+
# recommended technique for allowing sockets to communicate between
|
28
|
+
# threads.
|
29
|
+
#
|
30
|
+
# context = ZMQ::Context.create
|
31
|
+
# if context
|
32
|
+
# socket = context.socket(ZMQ::REQ)
|
33
|
+
# if socket
|
34
|
+
# ...
|
35
|
+
# else
|
36
|
+
# STDERR.puts "Socket allocation failed"
|
37
|
+
# end
|
38
|
+
# else
|
39
|
+
# STDERR.puts "Context allocation failed"
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
#
|
9
43
|
class Context
|
10
44
|
include ZMQ::Util
|
11
45
|
|
12
46
|
attr_reader :context, :pointer
|
13
47
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# The +io_threads+ number specifies the size of the thread pool
|
21
|
-
# allocated by 0mq for processing incoming/outgoing messages.
|
22
|
-
#
|
23
|
-
# Returns a context object. It's necessary for passing to the
|
24
|
-
# #Socket constructor when allocating new sockets. All sockets
|
25
|
-
# live within a context. Sockets in one context may not be accessed
|
26
|
-
# from another context; doing so raises an exception.
|
27
|
-
#
|
28
|
-
# Also, Sockets should *only* be accessed from the thread where they
|
29
|
-
# were first created. Do *not* pass sockets between threads; pass
|
30
|
-
# in the context and allocate a new socket per thread.
|
31
|
-
#
|
32
|
-
# To connect sockets between contexts, use +inproc+ or +ipc+
|
33
|
-
# transport and set up a 0mq socket between them. This is also the
|
34
|
-
# recommended technique for allowing sockets to communicate between
|
35
|
-
# threads.
|
36
|
-
#
|
37
|
-
# Will raise a #ContextError when the native library context cannot be
|
38
|
-
# be allocated.
|
48
|
+
def self.create io_threads = 1
|
49
|
+
new(io_threads) rescue nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Use the factory method Context#create to make contexts.
|
39
53
|
#
|
40
54
|
def initialize io_threads = 1
|
41
55
|
@sockets = []
|
42
56
|
@context = LibZMQ.zmq_init io_threads
|
43
57
|
@pointer = @context
|
44
|
-
error_check
|
58
|
+
error_check 'zmq_init', (@context.nil? || @context.null?) ? -1 : 0
|
45
59
|
|
46
60
|
define_finalizer
|
47
61
|
end
|
48
62
|
|
49
63
|
# Call to release the context and any remaining data associated
|
50
64
|
# with past sockets. This will close any sockets that remain
|
51
|
-
# open; further calls to those sockets will
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# Returns nil.
|
65
|
+
# open; further calls to those sockets will return -1 to indicate
|
66
|
+
# the operation failed.
|
55
67
|
#
|
56
|
-
#
|
57
|
-
# the context has somehow become null (indicates a libzmq bug).
|
68
|
+
# Returns 0 for success, -1 for failure.
|
58
69
|
#
|
59
70
|
def terminate
|
60
71
|
unless @context.nil? || @context.null?
|
61
|
-
|
62
|
-
|
72
|
+
remove_finalizer
|
73
|
+
rc = LibZMQ.zmq_term @context
|
63
74
|
@context = nil
|
64
75
|
@sockets = nil
|
65
|
-
|
76
|
+
rc
|
77
|
+
else
|
78
|
+
0
|
66
79
|
end
|
67
|
-
nil
|
68
80
|
end
|
69
81
|
|
70
82
|
# Short-cut to allocate a socket for a specific context.
|
@@ -80,13 +92,17 @@ module ZMQ
|
|
80
92
|
# #ZMQ::DEALER
|
81
93
|
# #ZMQ::ROUTER
|
82
94
|
#
|
83
|
-
# Returns a #ZMQ::Socket
|
84
|
-
#
|
85
|
-
# May raise a #ContextError or #SocketError.
|
95
|
+
# Returns a #ZMQ::Socket when the allocation succeeds, nil
|
96
|
+
# if it fails.
|
86
97
|
#
|
87
98
|
def socket type
|
88
|
-
sock =
|
89
|
-
|
99
|
+
sock = nil
|
100
|
+
begin
|
101
|
+
sock = Socket.new @context, type
|
102
|
+
rescue ContextError => e
|
103
|
+
sock = nil
|
104
|
+
end
|
105
|
+
|
90
106
|
sock
|
91
107
|
end
|
92
108
|
|