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 CHANGED
@@ -1,7 +1,9 @@
1
1
  *.gem
2
2
  .bundle
3
+ .yardoc/*
3
4
  Gemfile.lock
4
5
  pkg/*
6
+ doc/*
5
7
 
6
8
  *.rbc
7
9
  .redcar/
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-18mode
6
+ - jruby-19mode
7
+ - rbx-18mode
8
+ - rbx-19mode
9
+ notifications:
10
+ email: false
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --no-private
2
+ --protected
3
+ --markup="textile" lib/**/*.rb
4
+ --main README.textile
5
+ --asset examples:examples
6
+ --hide-tag todo
7
+ -
8
+ LICENSE
9
+ CHANGELOG
data/CHANGELOG ADDED
@@ -0,0 +1,11 @@
1
+ = Version 1.0.1
2
+
3
+ * Re-working of some ZeroMQ examples for use with Crossroads I/O
4
+ * Addition of missing socket option XS::IPV4ONLY
5
+ * Addition of new socket option XS::KEEPALIVE
6
+ * Editing of inline comments for Yard documentation in Textile
7
+ * Amended .gemspec to exclude ext/libxs.so from build to accommodate Travis CI in development
8
+
9
+ = Version 1.0.0
10
+
11
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 - 2012 Chuck Remes, Chris Duncan and contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ 'Software'), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,86 @@
1
+ h1. About ffi-rxs
2
+
3
+ This gem wraps the "Crossroads I/O":http://crossroads.io networking library using the Ruby FFI (foreign function interface). It's a pure Ruby wrapper so this gem can be loaded and run by any Ruby runtime that supports FFI. That's all of them: MRI 1.9.x, Rubinius and JRuby.
4
+
5
+ Crossroads I/O is a fork of ZeroMQ. This gem is a re-working of the ffi-rzmq gem created by Chuck Remes to provide bindings for the Crossroads I/O libxs C library instead of the ZeroMQ libzmq library. The gem auto-configures itself to expose the API conforming to the loaded C library.
6
+
7
+ h2. Features/Problems
8
+
9
+ This gem needs to be tested in the wild. Please kick its tyres and give it a good thrashing. It is inevitable that bugs will be discovered, so please open issues for them here or fork this project, fix them, and send me a pull request.
10
+
11
+ The 'ffi' gem has dropped support for MRI 1.8.x. Since this project relies on that gem to load and run this code, then this project does not support MRI 1.8.x. I recommend JRuby for the best performance and stability.
12
+
13
+ h2. Requirements
14
+
15
+ * Crossroads I/O version 1.0.0 or later.
16
+ * ffi (>= 1.0.0)
17
+
18
+ The Crossroads I/O library must be installed on your system in a well-known location like /usr/local/lib. This is the default for new Crossroads I/O installs. Future releases may include the library as a C extension built at time of installation.
19
+
20
+ Do *not* run this gem under MRI with an old 'ffi' gem. It will crash randomly and you will be sad.
21
+
22
+ h2. Installation
23
+
24
+ A full gem has been released to Rubygems.org as of release 1.0.0. Make sure the Crossroads I/O library is already installed on your system.
25
+
26
+ * gem install ffi-rxs # should grab the latest release
27
+
28
+
29
+ To build from git master:
30
+
31
+ * git clone git://github.com/celldee/ffi-rxs
32
+ * cd ffi-rxs
33
+ * gem build ffi-rxs.gemspec
34
+ * gem install ffi-rxs-*.gem
35
+
36
+
37
+ *NOTE* for Windows users!
38
+ In order for this gem to find the libxs.dll, it *must* be on the Windows PATH. Google for "modify windows path" for instructions on how to do that if you are unfamiliar with that activity.
39
+
40
+ h2. Examples
41
+
42
+ h3. Using Request (REQ) socket -
43
+
44
+ <pre>
45
+ require 'ffi-rxs'
46
+
47
+ ctx = XS::Context.create()
48
+ socket = ctx.socket(XS::REQ)
49
+ socket.connect("tcp://127.0.0.1:5000")
50
+
51
+ for i in 1..10
52
+ msg = "msg #{i.to_s}"
53
+ socket.send_string(msg)
54
+ puts "Sending: " + msg
55
+ socket.recv_string(msg_in = '')
56
+ puts "Received: " + msg_in
57
+ end
58
+ </pre>
59
+
60
+ h3. Using Reply (REP) socket -
61
+
62
+ <pre>
63
+ require 'ffi-rxs'
64
+
65
+ ctx = XS::Context.create()
66
+ socket = ctx.socket(XS::REP)
67
+ socket.bind("tcp://127.0.0.1:5000")
68
+
69
+ while true do
70
+ socket.recv_string(msg = '')
71
+ puts "Got: " + msg
72
+ socket.send_string(msg)
73
+ end
74
+ </pre>
75
+
76
+ The above examples and additional ones are available in the repository examples folder.
77
+
78
+ h2. Links
79
+
80
+ * "Source code":http://github.com/celldee/ffi-rxs
81
+ * "Crossroads I/O website":http://crossroads.io
82
+ * "Crossroads I/O Discussion Group":http://groups.crossroads.io/
83
+
84
+ h2. License
85
+
86
+ This project is licensed under the MIT license. Please see the LICENSE file in the repository.
data/examples/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010-2012 iMatix Corporation, Chris Duncan and Contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/examples/README ADDED
@@ -0,0 +1,2 @@
1
+ Contains some Ruby examples reworked from http://github.com/imatix/zguide
2
+ See LICENSE in examples directory
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ # Durable publisher to be used in conjunction with durable_sub.rb
4
+ # Justin Case <justin@playelite.com>
5
+
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
7
+
8
+ context = XS::Context.create()
9
+
10
+ # Subscriber tells us when it's ready here
11
+ sync = context.socket(XS::PULL)
12
+ sync.bind("tcp://127.0.0.1:5564")
13
+
14
+ # We send updates via this socket
15
+ publisher = context.socket(XS::PUB)
16
+ publisher.bind("tcp://127.0.0.1:5565")
17
+
18
+ # Wait for synchronization request
19
+ sync.recv_string(sync_request = '')
20
+
21
+ # Now broadcast exactly 10 updates with pause
22
+ 10.times do |update_number|
23
+ message = sprintf("Update %d", update_number)
24
+ puts "Sending: " + message
25
+ publisher.send_string(message)
26
+ sleep(1)
27
+ end
28
+
29
+ publisher.send_string("END")
30
+
31
+ sync.close
32
+ publisher.close
33
+ context.terminate
34
+
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ # Durable subscriber to be used in conjunction with durable_pub.rb
4
+ # Justin Case <justin@playelite.com>
5
+
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
7
+
8
+ context = XS::Context.create()
9
+
10
+ # Connect our subscriber socket
11
+ subscriber = context.socket(XS::SUB)
12
+ subscriber.setsockopt(XS::IDENTITY, "Hello")
13
+ subscriber.setsockopt(XS::SUBSCRIBE, "")
14
+ subscriber.connect("tcp://127.0.0.1:5565")
15
+
16
+ # Synchronize with publisher
17
+ sync = context.socket(XS::PUSH)
18
+ sync.connect("tcp://127.0.0.1:5564")
19
+ sync.send_string("")
20
+
21
+ # Get updates, exit when told to do so
22
+ loop do
23
+ subscriber.recv_string(message = '')
24
+ puts "Received: " + message
25
+ if message == "END"
26
+ break
27
+ end
28
+ end
29
+
@@ -0,0 +1,140 @@
1
+ # encoding: utf-8
2
+
3
+ # latency_measurement.rb
4
+
5
+ # Within a single process, we start up two threads. One thread has a REQ (request)
6
+ # socket and the second thread has a REP (reply) socket. We measure the
7
+ # *round-trip* latency between these sockets. Only *one* message is in flight at
8
+ # any given moment.
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 latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
15
+ #
16
+ # % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
17
+
18
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
19
+
20
+ if ARGV.length < 3
21
+ puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
22
+ exit
23
+ end
24
+
25
+ link = ARGV[0]
26
+ message_size = ARGV[1].to_i
27
+ roundtrip_count = ARGV[2].to_i
28
+
29
+ def assert(rc)
30
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
31
+ end
32
+
33
+ begin
34
+ master_context = XS::Context.new
35
+ rescue ContextError => e
36
+ STDERR.puts "Failed to allocate context or socket!"
37
+ raise
38
+ end
39
+
40
+
41
+ class Receiver
42
+ def initialize context, link, size, count
43
+ @context = context
44
+ @link = link
45
+ @size = size
46
+ @count = count
47
+
48
+ begin
49
+ @socket = @context.socket(XS::REP)
50
+ rescue ContextError => e
51
+ STDERR.puts "Failed to allocate REP socket!"
52
+ raise
53
+ end
54
+
55
+ assert(@socket.setsockopt(XS::LINGER, 100))
56
+ assert(@socket.setsockopt(XS::RCVHWM, 100))
57
+ assert(@socket.setsockopt(XS::SNDHWM, 100))
58
+
59
+ assert(@socket.bind(@link))
60
+ end
61
+
62
+ def run
63
+ @count.times do
64
+ string = ''
65
+ assert(@socket.recv_string(string, 0))
66
+
67
+ raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
68
+
69
+ assert(@socket.send_string(string, 0))
70
+ end
71
+
72
+ assert(@socket.close)
73
+ end
74
+ end
75
+
76
+ class Transmitter
77
+ def initialize context, link, size, count
78
+ @context = context
79
+ @link = link
80
+ @size = size
81
+ @count = count
82
+
83
+ begin
84
+ @socket = @context.socket(XS::REQ)
85
+ rescue ContextError => e
86
+ STDERR.puts "Failed to allocate REP socket!"
87
+ raise
88
+ end
89
+
90
+ assert(@socket.setsockopt(XS::LINGER, 100))
91
+ assert(@socket.setsockopt(XS::RCVHWM, 100))
92
+ assert(@socket.setsockopt(XS::SNDHWM, 100))
93
+
94
+ assert(@socket.connect(@link))
95
+ end
96
+
97
+ def run
98
+ msg = "#{ '3' * @size }"
99
+
100
+ elapsed = elapsed_microseconds do
101
+ @count.times do
102
+ assert(@socket.send_string(msg, 0))
103
+ assert(@socket.recv_string(msg, 0))
104
+
105
+ raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
106
+ end
107
+ end
108
+
109
+ latency = elapsed / @count / 2
110
+
111
+ puts "message size: %i [B]" % @size
112
+ puts "roundtrip count: %i" % @count
113
+ puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
114
+ puts "mean latency: %.3f [us]" % latency
115
+ assert(@socket.close)
116
+ end
117
+
118
+ def elapsed_microseconds(&blk)
119
+ start = Time.now
120
+ yield
121
+ value = ((Time.now - start) * 1_000_000)
122
+ end
123
+ end
124
+
125
+ threads = []
126
+ threads << Thread.new do
127
+ receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
128
+ receiver.run
129
+ end
130
+
131
+ sleep 1
132
+
133
+ threads << Thread.new do
134
+ transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
135
+ transmitter.run
136
+ end
137
+
138
+ threads.each {|t| t.join}
139
+
140
+ master_context.terminate
data/examples/reply.rb ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ # reply.rb
4
+
5
+ # This example is used in conjunction with request.rb. Run this program in
6
+ # a terminal/console window and then run request.rb in another terminal/console
7
+ # window and observe the output.
8
+ #
9
+ # Usage: ruby reply.rb
10
+ #
11
+ # To stop the program terminate the process with Ctrl-C or another
12
+ # method of your choice.
13
+ #
14
+
15
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
16
+
17
+ ctx = XS::Context.create()
18
+ socket = ctx.socket(XS::REP)
19
+ socket.bind("tcp://127.0.0.1:5000")
20
+
21
+ while true do
22
+ socket.recv_string(msg = '')
23
+ puts "Got: " + msg
24
+ socket.send_string(msg)
25
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ # req_rep_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
+
13
+ def assert(rc)
14
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
15
+ end
16
+
17
+ link = "tcp://127.0.0.1:5554"
18
+
19
+ begin
20
+ ctx = XS::Context.create()
21
+ s1 = ctx.socket(XS::REQ)
22
+ s2 = ctx.socket(XS::REP)
23
+ rescue ContextError => e
24
+ STDERR.puts "Failed to allocate context or socket!"
25
+ raise
26
+ end
27
+
28
+ assert(s1.setsockopt(XS::LINGER, 100))
29
+ assert(s2.setsockopt(XS::LINGER, 100))
30
+
31
+ assert(s1.connect(link))
32
+ assert(s2.bind(link))
33
+
34
+ poller = XS::Poller.new
35
+ poller.register_readable(s2)
36
+ poller.register_writable(s1)
37
+
38
+ start_time = Time.now
39
+ @unsent = true
40
+
41
+ until @done do
42
+ assert(poller.poll_nonblock)
43
+
44
+ # send the message after 5 seconds
45
+ if Time.now - start_time > 5 && @unsent
46
+ payload = "#{ '3' * 1024 }"
47
+
48
+ puts "sending payload nonblocking"
49
+ assert(s1.send_string(payload, XS::NonBlocking))
50
+ @unsent = false
51
+ end
52
+
53
+ # check for messages after 1 second
54
+ if Time.now - start_time > 1
55
+ poller.readables.each do |sock|
56
+ received_msg = ''
57
+ assert(sock.recv_string(received_msg, XS::NonBlocking))
58
+
59
+ puts "message received [#{received_msg}]"
60
+ @done = true
61
+ end
62
+ end
63
+ end
64
+
65
+ puts "executed in [#{Time.now - start_time}] seconds"
66
+
67
+ assert(s1.close)
68
+ assert(s2.close)
69
+
70
+ ctx.terminate
71
+
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ # request.rb
4
+
5
+ # This example is used in conjunction with reply.rb. Run this program in
6
+ # a terminal/console window and then run reply.rb in another terminal/console
7
+ # window and observe the output.
8
+ #
9
+ # Usage: ruby request.rb
10
+ #
11
+
12
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
13
+
14
+ ctx = XS::Context.create()
15
+ socket = ctx.socket(XS::REQ)
16
+ socket.connect("tcp://127.0.0.1:5000")
17
+
18
+ for i in 1..10
19
+ msg = "msg #{i.to_s}"
20
+ socket.send_string(msg)
21
+ puts "Sending: " + msg
22
+ socket.recv_string(msg_in = '')
23
+ puts "Received: " + msg_in
24
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ # Task sink to be used in conjunction with task_vent.rb
4
+ # and task_worker.rb
5
+ # Binds PULL socket to tcp://localhost:5558
6
+ # Collects results from task_workers via that socket
7
+
8
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
9
+
10
+ # Prepare our context and socket
11
+ context = XS::Context.create()
12
+ receiver = context.socket(XS::PULL)
13
+ receiver.bind("tcp://*:5558")
14
+
15
+ # Wait for start of batch
16
+ receiver.recv_string('')
17
+ puts 'Sink started'
18
+ tstart = Time.now
19
+
20
+ # Process 100 confirmations
21
+ 100.times do |task_nbr|
22
+ receiver.recv_string('')
23
+ $stdout << ((task_nbr % 10 == 0) ? ':' : '.')
24
+ $stdout.flush
25
+ end
26
+
27
+ # Calculate and report duration of batch
28
+ tend = Time.now
29
+ total_msec = (tend-tstart) * 1000
30
+ puts "Total elapsed time: #{total_msec} msec"
31
+
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ # Task vent to be used in conjunction with task_worker.rb
4
+ # and task_sink.rb
5
+ # Binds PUSH socket to tcp://localhost:5557
6
+ # Sends batch of tasks to task_workers via that socket
7
+
8
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
9
+
10
+ context = XS::Context.create()
11
+
12
+ # Socket to send messages on
13
+ sender = context.socket(XS::PUSH)
14
+ sender.bind("tcp://*:5557")
15
+
16
+ puts "Press enter when the workers are ready..."
17
+ $stdin.read(1)
18
+ puts "Sending tasks to workers..."
19
+
20
+ # The first message is "0" and signals start of batch
21
+ sender.send_string('0')
22
+
23
+ # Send 100 tasks
24
+ total_msec = 0 # Total expected cost in msecs
25
+ 100.times do
26
+ workload = rand(100) + 1
27
+ total_msec += workload
28
+ $stdout << "#{workload}."
29
+ sender.send_string(workload.to_s)
30
+ end
31
+
32
+ puts "Total expected cost: #{total_msec} msec"
33
+ Kernel.sleep(1) # Give Crossroads time to deliver
34
+
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ # Task worker to be used in conjunction with task_vent.rb
4
+ # and task_sink.rb
5
+ # Connects PULL socket to tcp://localhost:5557
6
+ # Collects workloads from task_vent via that socket
7
+ # Connects PUSH socket to tcp://localhost:5558
8
+ # Sends results to sink via that socket
9
+
10
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
11
+
12
+ context = XS::Context.create()
13
+
14
+ # Socket to receive messages on
15
+ receiver = context.socket(XS::PULL)
16
+ receiver.connect("tcp://localhost:5557")
17
+
18
+ # Socket to send messages to
19
+ sender = context.socket(XS::PUSH)
20
+ sender.connect("tcp://localhost:5558")
21
+
22
+ # Process tasks forever
23
+ while true
24
+
25
+ receiver.recv_string(msec = '')
26
+ # Simple progress indicator for the viewer
27
+ $stdout << "#{msec}."
28
+ $stdout.flush
29
+
30
+ # Do the work
31
+ sleep(msec.to_f / 1000)
32
+
33
+ # Send results to sink
34
+ sender.send_string("")
35
+ end
36
+