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