ffi-rxs 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +9 -0
- data/CHANGELOG +11 -0
- data/LICENSE +20 -0
- data/README.textile +86 -0
- data/examples/LICENSE +18 -0
- data/examples/README +2 -0
- data/examples/durable_pub.rb +34 -0
- data/examples/durable_sub.rb +29 -0
- data/examples/latency_measurement.rb +140 -0
- data/examples/reply.rb +25 -0
- data/examples/req_rep_poll.rb +71 -0
- data/examples/request.rb +24 -0
- data/examples/task_sink.rb +31 -0
- data/examples/task_vent.rb +34 -0
- data/examples/task_worker.rb +36 -0
- data/examples/throughput_measurement.rb +169 -0
- data/examples/weather_upd_client.rb +47 -0
- data/examples/weather_upd_server.rb +35 -0
- data/examples/xreq_xrep_poll.rb +101 -0
- data/ffi-rxs.gemspec +3 -5
- data/lib/ffi-rxs/constants.rb +15 -11
- data/lib/ffi-rxs/context.rb +54 -60
- data/lib/ffi-rxs/exceptions.rb +7 -5
- data/lib/ffi-rxs/libc.rb +1 -0
- data/lib/ffi-rxs/libxs.rb +2 -0
- data/lib/ffi-rxs/message.rb +79 -50
- data/lib/ffi-rxs/poll.rb +54 -12
- data/lib/ffi-rxs/poll_items.rb +2 -1
- data/lib/ffi-rxs/socket.rb +243 -184
- data/lib/ffi-rxs/util.rb +25 -16
- data/lib/ffi-rxs/version.rb +3 -1
- data/lib/ffi-rxs.rb +8 -14
- data/spec/socket_spec.rb +63 -0
- metadata +31 -19
- data/README.rdoc +0 -86
@@ -0,0 +1,169 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# throughput_measurement.rb
|
4
|
+
|
5
|
+
# Within a single process, we start up five threads. Main thread has a PUB (publisher)
|
6
|
+
# socket and the secondary threads have SUB (subscription) sockets. We measure the
|
7
|
+
# *throughput* between these sockets. A high-water mark (HWM) is *not* set, so the
|
8
|
+
# publisher queue is free to grow to the size of memory without dropping packets.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby throughput_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby throughput_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
|
18
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
19
|
+
require 'thread'
|
20
|
+
|
21
|
+
if ARGV.length < 3
|
22
|
+
puts "usage: ruby throughput_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
link = ARGV[0]
|
27
|
+
message_size = ARGV[1].to_i
|
28
|
+
count = ARGV[2].to_i
|
29
|
+
|
30
|
+
def assert(rc)
|
31
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
master_context = XS::Context.new
|
36
|
+
rescue ContextError => e
|
37
|
+
STDERR.puts "Failed to allocate context or socket!"
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
class Receiver
|
43
|
+
def initialize context, link, size, count, stats
|
44
|
+
@context = context
|
45
|
+
@link = link
|
46
|
+
@size = size
|
47
|
+
@count = count
|
48
|
+
@stats = stats
|
49
|
+
|
50
|
+
begin
|
51
|
+
@socket = @context.socket(XS::SUB)
|
52
|
+
rescue ContextError => e
|
53
|
+
STDERR.puts "Failed to allocate SUB socket!"
|
54
|
+
raise
|
55
|
+
end
|
56
|
+
|
57
|
+
assert(@socket.setsockopt(XS::LINGER, 100))
|
58
|
+
assert(@socket.setsockopt(XS::SUBSCRIBE, ""))
|
59
|
+
|
60
|
+
assert(@socket.connect(@link))
|
61
|
+
end
|
62
|
+
|
63
|
+
def run
|
64
|
+
msg = XS::Message.new
|
65
|
+
assert(@socket.recvmsg(msg))
|
66
|
+
|
67
|
+
elapsed = elapsed_microseconds do
|
68
|
+
(@count - 1).times do
|
69
|
+
assert(@socket.recvmsg(msg))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@stats.record_elapsed(elapsed)
|
74
|
+
assert(@socket.close)
|
75
|
+
end
|
76
|
+
|
77
|
+
def elapsed_microseconds(&blk)
|
78
|
+
start = Time.now
|
79
|
+
yield
|
80
|
+
((Time.now - start) * 1_000_000)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Transmitter
|
85
|
+
def initialize context, link, size, count
|
86
|
+
@context = context
|
87
|
+
@link = link
|
88
|
+
@size = size
|
89
|
+
@count = count
|
90
|
+
|
91
|
+
begin
|
92
|
+
@socket = @context.socket(XS::PUB)
|
93
|
+
rescue ContextError => e
|
94
|
+
STDERR.puts "Failed to allocate PUB socket!"
|
95
|
+
raise
|
96
|
+
end
|
97
|
+
|
98
|
+
assert(@socket.setsockopt(XS::LINGER, 100))
|
99
|
+
assert(@socket.bind(@link))
|
100
|
+
end
|
101
|
+
|
102
|
+
def run
|
103
|
+
sleep 1
|
104
|
+
contents = "#{'0' * @size}"
|
105
|
+
|
106
|
+
i = 0
|
107
|
+
while i < @count
|
108
|
+
msg = XS::Message.new(contents)
|
109
|
+
assert(@socket.sendmsg(msg))
|
110
|
+
i += 1
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
def close
|
116
|
+
assert(@socket.close)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Stats
|
121
|
+
def initialize size, count
|
122
|
+
@size = size
|
123
|
+
@count = count
|
124
|
+
|
125
|
+
@mutex = Mutex.new
|
126
|
+
@elapsed = []
|
127
|
+
end
|
128
|
+
|
129
|
+
def record_elapsed(elapsed)
|
130
|
+
@mutex.synchronize do
|
131
|
+
@elapsed << elapsed
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def output
|
136
|
+
@elapsed.each do |elapsed|
|
137
|
+
throughput = @count * 1000000 / elapsed
|
138
|
+
megabits = throughput * @size * 8 / 1000000
|
139
|
+
|
140
|
+
puts "message size: %i [B]" % @size
|
141
|
+
puts "message count: %i" % @count
|
142
|
+
puts "mean throughput: %i [msg/s]" % throughput
|
143
|
+
puts "mean throughput: %.3f [Mb/s]" % megabits
|
144
|
+
puts
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
threads = []
|
150
|
+
stats = Stats.new message_size, count
|
151
|
+
transmitter = Transmitter.new(master_context, link, message_size, count)
|
152
|
+
|
153
|
+
threads << Thread.new do
|
154
|
+
transmitter.run
|
155
|
+
end
|
156
|
+
|
157
|
+
1.times do
|
158
|
+
threads << Thread.new do
|
159
|
+
receiver = Receiver.new(master_context, link, message_size, count, stats)
|
160
|
+
receiver.run
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
threads.each {|t| t.join}
|
165
|
+
transmitter.close
|
166
|
+
stats.output
|
167
|
+
|
168
|
+
master_context.terminate
|
169
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# weather_upd_client.rb
|
4
|
+
|
5
|
+
# This example is used in conjunction with weather_upd_server.rb.
|
6
|
+
# Run this program in a terminal/console window and then run
|
7
|
+
# weather_upd_server.rb in another terminal/console window and
|
8
|
+
# observe the output.
|
9
|
+
#
|
10
|
+
# This program subscribes to a feed of weather updates published
|
11
|
+
# by weather_upd_server.rb, collects the first 100 updates that
|
12
|
+
# match the subscription filter and displays the average temperature
|
13
|
+
# for that zipcode.
|
14
|
+
#
|
15
|
+
# Usage: ruby weather_upd_client.rb [zip code (default=10001)]
|
16
|
+
#
|
17
|
+
# If you supply a zip code argument then the maximum value that will
|
18
|
+
# be recognized is 11000.
|
19
|
+
|
20
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
21
|
+
|
22
|
+
COUNT = 100
|
23
|
+
|
24
|
+
context = XS::Context.create()
|
25
|
+
|
26
|
+
# Socket to talk to server
|
27
|
+
puts "Collecting updates from weather server..."
|
28
|
+
subscriber = context.socket(XS::SUB)
|
29
|
+
subscriber.connect("tcp://127.0.0.1:5556")
|
30
|
+
|
31
|
+
# Subscribe to zipcode, default is NYC, 10001
|
32
|
+
filter = ARGV.size > 0 ? ARGV[0] : "10001"
|
33
|
+
subscriber.setsockopt(XS::SUBSCRIBE, filter)
|
34
|
+
|
35
|
+
# Process 100 updates
|
36
|
+
total_temp = 0
|
37
|
+
1.upto(COUNT) do |update_nbr|
|
38
|
+
s = ''
|
39
|
+
subscriber.recv_string(s)
|
40
|
+
|
41
|
+
zipcode, temperature, relhumidity = s.split.map(&:to_i)
|
42
|
+
total_temp += temperature
|
43
|
+
puts "Update #{update_nbr.to_s}: #{temperature.to_s}F"
|
44
|
+
end
|
45
|
+
|
46
|
+
puts "Average temperature for zipcode '#{filter}' was #{total_temp / COUNT}F"
|
47
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# weather_upd_server.rb
|
4
|
+
|
5
|
+
# This example is used in conjunction with weather_upd_client.rb.
|
6
|
+
# Run this program in a terminal/console window and then run
|
7
|
+
# weather_upd_client.rb in another terminal/console window and
|
8
|
+
# observe the output.
|
9
|
+
#
|
10
|
+
# This program publishes a feed of random weather updates containing
|
11
|
+
# random zip codes up to a maximum value of 11000.
|
12
|
+
#
|
13
|
+
# Usage: ruby weather_upd_server.rb
|
14
|
+
#
|
15
|
+
# To stop the program terminate the process with Ctrl-C or another
|
16
|
+
# method of your choice.
|
17
|
+
#
|
18
|
+
|
19
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
20
|
+
|
21
|
+
context = XS::Context.create()
|
22
|
+
publisher = context.socket(XS::PUB)
|
23
|
+
publisher.bind("tcp://127.0.0.1:5556")
|
24
|
+
|
25
|
+
while true
|
26
|
+
# Get values that will fool the boss
|
27
|
+
zipcode = rand(11000)
|
28
|
+
temperature = rand(215) - 80
|
29
|
+
relhumidity = rand(50) + 10
|
30
|
+
|
31
|
+
update = "%05d %d %d" % [zipcode, temperature, relhumidity]
|
32
|
+
puts update
|
33
|
+
publisher.send_string(update)
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# xreq_xrep_poll.rb
|
4
|
+
#
|
5
|
+
# It illustrates the use of xs_poll(), as wrapped by the Ruby library,
|
6
|
+
# for detecting and responding to read and write events recorded on sockets.
|
7
|
+
# It also shows how to use XS::NO_BLOCK/XS::DONTWAIT for non-blocking send
|
8
|
+
# and receive.
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
11
|
+
|
12
|
+
def assert(rc)
|
13
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
14
|
+
end
|
15
|
+
|
16
|
+
link = "tcp://127.0.0.1:5555"
|
17
|
+
|
18
|
+
|
19
|
+
begin
|
20
|
+
ctx = XS::Context.new
|
21
|
+
s1 = ctx.socket(XS::XREQ)
|
22
|
+
s2 = ctx.socket(XS::XREP)
|
23
|
+
rescue ContextError => e
|
24
|
+
STDERR.puts "Failed to allocate context or socket"
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
s1.identity = 'socket1.xreq'
|
29
|
+
s2.identity = 'socket2.xrep'
|
30
|
+
|
31
|
+
assert(s1.setsockopt(XS::LINGER, 100))
|
32
|
+
assert(s2.setsockopt(XS::LINGER, 100))
|
33
|
+
|
34
|
+
assert(s1.bind(link))
|
35
|
+
assert(s2.connect(link))
|
36
|
+
|
37
|
+
poller = XS::Poller.new
|
38
|
+
poller.register_readable(s2)
|
39
|
+
poller.register_writable(s1)
|
40
|
+
|
41
|
+
start_time = Time.now
|
42
|
+
@unsent = true
|
43
|
+
|
44
|
+
until @done do
|
45
|
+
assert(poller.poll_nonblock)
|
46
|
+
|
47
|
+
# send the message after 5 seconds
|
48
|
+
if Time.now - start_time > 5 && @unsent
|
49
|
+
puts "sending payload nonblocking"
|
50
|
+
|
51
|
+
5.times do |i|
|
52
|
+
payload = "#{ i.to_s * 40 }"
|
53
|
+
assert(s1.send_string(payload, XS::NonBlocking))
|
54
|
+
end
|
55
|
+
@unsent = false
|
56
|
+
end
|
57
|
+
|
58
|
+
# check for messages after 1 second
|
59
|
+
if Time.now - start_time > 1
|
60
|
+
poller.readables.each do |sock|
|
61
|
+
|
62
|
+
if sock.identity =~ /xrep/
|
63
|
+
routing_info = ''
|
64
|
+
assert(sock.recv_string(routing_info, XS::NonBlocking))
|
65
|
+
puts "routing_info received [#{routing_info}] on socket.identity [#{sock.identity}]"
|
66
|
+
else
|
67
|
+
routing_info = nil
|
68
|
+
received_msg = ''
|
69
|
+
assert(sock.recv_string(received_msg, XS::NonBlocking))
|
70
|
+
|
71
|
+
# skip to the next iteration if received_msg is nil; that means we got an EAGAIN
|
72
|
+
next unless received_msg
|
73
|
+
puts "message received [#{received_msg}] on socket.identity [#{sock.identity}]"
|
74
|
+
end
|
75
|
+
|
76
|
+
while sock.more_parts? do
|
77
|
+
received_msg = ''
|
78
|
+
assert(sock.recv_string(received_msg, XS::NonBlocking))
|
79
|
+
|
80
|
+
puts "message received [#{received_msg}]"
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "kick back a reply"
|
84
|
+
assert(sock.send_string(routing_info, XS::SNDMORE | XS::NonBlocking)) if routing_info
|
85
|
+
time = Time.now.strftime "%Y-%m-%dT%H:%M:%S.#{Time.now.usec}"
|
86
|
+
reply = "reply " + sock.identity.upcase + " #{time}"
|
87
|
+
puts "sent reply [#{reply}], #{time}"
|
88
|
+
assert(sock.send_string(reply))
|
89
|
+
@done = true
|
90
|
+
poller.register_readable(s1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
puts "executed in [#{Time.now - start_time}] seconds"
|
96
|
+
|
97
|
+
assert(s1.close)
|
98
|
+
assert(s2.close)
|
99
|
+
|
100
|
+
ctx.terminate
|
101
|
+
|
data/ffi-rxs.gemspec
CHANGED
@@ -8,13 +8,11 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Chris Duncan"]
|
9
9
|
s.email = ["celldee@gmail.com"]
|
10
10
|
s.homepage = "http://github.com/celldee/ffi-rxs"
|
11
|
-
s.summary = %q{
|
12
|
-
s.description = %q{
|
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:
|
15
|
-
MRI 1.9.x, Rubinius and JRuby.}
|
11
|
+
s.summary = %q{Ruby FFI bindings for Crossroads I/O networking library.}
|
12
|
+
s.description = %q{Ruby FFI bindings for Crossroads I/O networking library.}
|
16
13
|
|
17
14
|
s.files = `git ls-files`.split("\n")
|
15
|
+
s.files = s.files.reject{ |f| f.include?('ext/libxs.so') }
|
18
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
18
|
s.require_paths = ["lib"]
|
data/lib/ffi-rxs/constants.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module XS
|
2
4
|
# Set up all of the constants
|
3
5
|
|
@@ -5,7 +7,7 @@ module XS
|
|
5
7
|
MAX_SOCKETS = 1
|
6
8
|
IO_THREADS = 2
|
7
9
|
|
8
|
-
#
|
10
|
+
# Socket types
|
9
11
|
PAIR = 0
|
10
12
|
PUB = 1
|
11
13
|
SUB = 2
|
@@ -36,7 +38,7 @@ module XS
|
|
36
38
|
XSUB => "XSUB"
|
37
39
|
}
|
38
40
|
|
39
|
-
#
|
41
|
+
# Socket options
|
40
42
|
AFFINITY = 4
|
41
43
|
IDENTITY = 5
|
42
44
|
SUBSCRIBE = 6
|
@@ -59,21 +61,23 @@ module XS
|
|
59
61
|
MULTICAST_HOPS = 25
|
60
62
|
RCVTIMEO = 27
|
61
63
|
SNDTIMEO = 28
|
64
|
+
IPV4ONLY = 31
|
65
|
+
KEEPALIVE = 32
|
66
|
+
|
67
|
+
# Message options
|
68
|
+
MORE = 1
|
62
69
|
|
63
|
-
#
|
70
|
+
# Send/recv options
|
64
71
|
DONTWAIT = 1
|
65
72
|
SNDMORE = 2
|
66
|
-
SNDLABEL = 4
|
67
73
|
NonBlocking = DONTWAIT
|
68
74
|
|
69
|
-
#
|
70
|
-
|
71
|
-
POLL = 1
|
75
|
+
# I/O multiplexing
|
72
76
|
POLLIN = 1
|
73
77
|
POLLOUT = 2
|
74
78
|
POLLERR = 4
|
75
79
|
|
76
|
-
#
|
80
|
+
# Socket errors
|
77
81
|
EAGAIN = Errno::EAGAIN::Errno
|
78
82
|
EFAULT = Errno::EFAULT::Errno
|
79
83
|
EINVAL = Errno::EINVAL::Errno
|
@@ -88,9 +92,9 @@ module XS
|
|
88
92
|
ENOCOMPATPROTO = (HAUSNUMERO + 52)
|
89
93
|
ETERM = (HAUSNUMERO + 53)
|
90
94
|
|
91
|
-
# Rescue unknown constants and use the Crossroads defined values
|
92
|
-
# Usually only happens on Windows
|
93
|
-
# OSX
|
95
|
+
# Rescue unknown constants and use the Crossroads defined values.
|
96
|
+
# Usually only happens on Windows although some do not resolve on
|
97
|
+
# OSX either _ENOTSUP_
|
94
98
|
ENOTSUP = Errno::ENOTSUP::Errno rescue (HAUSNUMERO + 1)
|
95
99
|
EPROTONOSUPPORT = Errno::EPROTONOSUPPORT::Errno rescue (HAUSNUMERO + 2)
|
96
100
|
ENOBUFS = Errno::ENOBUFS::Errno rescue (HAUSNUMERO + 3)
|
data/lib/ffi-rxs/context.rb
CHANGED
@@ -1,20 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
3
|
module XS
|
3
4
|
|
4
|
-
|
5
|
-
#
|
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.
|
5
|
+
# All sockets exist within a context and a context is passed to the
|
6
|
+
# Socket constructor when allocating new sockets.
|
18
7
|
#
|
19
8
|
# Also, Sockets should *only* be accessed from the thread where they
|
20
9
|
# were first created. Do *not* pass sockets between threads; pass
|
@@ -27,30 +16,29 @@ module XS
|
|
27
16
|
# recommended technique for allowing sockets to communicate between
|
28
17
|
# threads.
|
29
18
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
19
|
+
# @example Create context and socket
|
20
|
+
# context = XS::Context.create
|
21
|
+
# if context
|
22
|
+
# socket = context.socket(XS::REQ)
|
23
|
+
# if socket
|
24
|
+
# ...
|
25
|
+
# else
|
26
|
+
# STDERR.puts "Socket allocation failed"
|
27
|
+
# end
|
28
|
+
# else
|
29
|
+
# STDERR.puts "Context allocation failed"
|
30
|
+
# end
|
43
31
|
class Context
|
44
32
|
include XS::Util
|
45
33
|
|
46
34
|
attr_reader :context, :pointer
|
47
35
|
|
36
|
+
# Factory method to instantiate contexts
|
48
37
|
def self.create
|
49
38
|
new() rescue nil
|
50
39
|
end
|
51
40
|
|
52
|
-
#
|
53
|
-
#
|
41
|
+
# Initialize context object
|
54
42
|
def initialize
|
55
43
|
@sockets = []
|
56
44
|
@context = LibXS.xs_init()
|
@@ -60,25 +48,36 @@ module XS
|
|
60
48
|
define_finalizer
|
61
49
|
end
|
62
50
|
|
63
|
-
#
|
51
|
+
# Sets options on a context.
|
52
|
+
#
|
53
|
+
# It is recommended to use the default for +io_threads+
|
54
|
+
# (which is 1) since most programs will not saturate I/O.
|
64
55
|
#
|
65
|
-
#
|
66
|
-
#
|
56
|
+
# The rule of thumb is to make io_threads equal to the number
|
57
|
+
# of gigabits per second that the application will produce.
|
67
58
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# XS::MAX_SOCKETS
|
59
|
+
# The io_threads number specifies the size of the thread pool
|
60
|
+
# allocated by Crossroads for processing incoming/outgoing messages.
|
71
61
|
#
|
72
|
-
#
|
73
|
-
#
|
62
|
+
# The +max_sockets+ number specifies the number of concurrent
|
63
|
+
# sockets that can be used in the context. The default is 512.
|
74
64
|
#
|
75
|
-
#
|
76
|
-
#
|
65
|
+
# Context options take effect only if set with **setctxopt()** prior to
|
66
|
+
# creating the first socket in a given context with **socket()**.
|
77
67
|
#
|
78
|
-
#
|
79
|
-
#
|
68
|
+
# @param [Constant] name
|
69
|
+
# One of @XS::IO_THREADS@ or @XS::MAX_SOCKETS@.
|
70
|
+
# @param [Integer] value
|
71
|
+
#
|
72
|
+
# @return 0 when the operation completed successfully.
|
73
|
+
# @return -1 when this operation fails.
|
80
74
|
#
|
81
|
-
|
75
|
+
# @example Set io_threads context option
|
76
|
+
# rc = context.setctxopt(XS::IO_THREADS, 10)
|
77
|
+
# unless XS::Util.resultcode_ok?(rc)
|
78
|
+
# XS::raise_error('xs_setctxopt', rc)
|
79
|
+
# end
|
80
|
+
def setctxopt name, value
|
82
81
|
length = 4
|
83
82
|
pointer = LibC.malloc length
|
84
83
|
pointer.write_int value
|
@@ -88,13 +87,13 @@ module XS
|
|
88
87
|
rc
|
89
88
|
end
|
90
89
|
|
91
|
-
#
|
90
|
+
# Releases the context and any remaining data associated
|
92
91
|
# with past sockets. This will close any sockets that remain
|
93
92
|
# open; further calls to those sockets will return -1 to indicate
|
94
93
|
# the operation failed.
|
95
94
|
#
|
96
|
-
#
|
97
|
-
#
|
95
|
+
# @return 0 for success
|
96
|
+
# @return -1 for failure
|
98
97
|
def terminate
|
99
98
|
unless @context.nil? || @context.null?
|
100
99
|
remove_finalizer
|
@@ -107,22 +106,14 @@ module XS
|
|
107
106
|
end
|
108
107
|
end
|
109
108
|
|
110
|
-
#
|
109
|
+
# Allocates a socket for context
|
111
110
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
# #XS::PUB
|
116
|
-
# #XS::SUB
|
117
|
-
# #XS::PAIR
|
118
|
-
# #XS::PULL
|
119
|
-
# #XS::PUSH
|
120
|
-
# #XS::DEALER
|
121
|
-
# #XS::ROUTER
|
122
|
-
#
|
123
|
-
# Returns a #XS::Socket when the allocation succeeds, nil
|
124
|
-
# if it fails.
|
111
|
+
# @param [Constant] type
|
112
|
+
# One of @XS::REQ@, @XS::REP@, @XS::PUB@, @XS::SUB@, @XS::PAIR@,
|
113
|
+
# @XS::PULL@, @XS::PUSH@, @XS::DEALER@, or @XS::ROUTER@
|
125
114
|
#
|
115
|
+
# @return [Socket] when the allocation succeeds
|
116
|
+
# @return nil when call fails
|
126
117
|
def socket type
|
127
118
|
sock = nil
|
128
119
|
begin
|
@@ -137,14 +128,17 @@ module XS
|
|
137
128
|
|
138
129
|
private
|
139
130
|
|
131
|
+
# Deletes native resources after object has been destroyed
|
140
132
|
def define_finalizer
|
141
133
|
ObjectSpace.define_finalizer(self, self.class.close(@context))
|
142
134
|
end
|
143
|
-
|
135
|
+
|
136
|
+
# Removes all finalizers for object
|
144
137
|
def remove_finalizer
|
145
138
|
ObjectSpace.undefine_finalizer self
|
146
139
|
end
|
147
140
|
|
141
|
+
# Closes the context
|
148
142
|
def self.close context
|
149
143
|
Proc.new { LibXS.xs_term context unless context.null? }
|
150
144
|
end
|
data/lib/ffi-rxs/exceptions.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
3
|
module XS
|
3
|
-
|
4
|
+
# General Crossroads error class
|
4
5
|
class XSError < StandardError
|
5
6
|
attr_reader :source, :result_code, :error_code, :message
|
6
7
|
|
@@ -8,12 +9,13 @@ module XS
|
|
8
9
|
@source = source
|
9
10
|
@result_code = result_code
|
10
11
|
@error_code = error_code
|
11
|
-
@message = "
|
12
|
+
@message = "source [#{source}], msg [#{message}], " +
|
13
|
+
"error code [#{error_code}],rc [#{result_code}]"
|
12
14
|
super message
|
13
15
|
end
|
14
16
|
end # call XSError
|
15
17
|
|
16
|
-
|
18
|
+
# Context error class
|
17
19
|
class ContextError < XSError
|
18
20
|
# True when the exception was raised due to the library
|
19
21
|
# returning EINVAL.
|
@@ -33,12 +35,12 @@ module XS
|
|
33
35
|
|
34
36
|
end # class ContextError
|
35
37
|
|
36
|
-
|
38
|
+
# Message error class
|
37
39
|
class MessageError < XSError
|
38
40
|
# True when the exception was raised due to the library
|
39
41
|
# returning ENOMEM.
|
40
42
|
#
|
41
|
-
# Only ever raised by the
|
43
|
+
# Only ever raised by the Message class when it fails
|
42
44
|
# to allocate sufficient memory to send a message.
|
43
45
|
#
|
44
46
|
def enomem?() ENOMEM == @error_code; end
|
data/lib/ffi-rxs/libc.rb
CHANGED