ffi-rxs 1.0.0 → 1.0.1
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/.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