ffi-rzmq 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+ if ARGV.length < 3
5
+ puts "usage: remote_lat <connect-to> <message-size> <roundtrip-count>"
6
+ exit
7
+ end
8
+
9
+ connect_to = ARGV[0]
10
+ message_size = ARGV[1].to_i
11
+ roundtrip_count = ARGV[2].to_i
12
+
13
+ ctx = ZMQ::Context.new 1
14
+ s = ctx.socket ZMQ::REQ
15
+ s.connect connect_to
16
+
17
+ msg = ZMQ::Message.new "#{'3'*message_size}"
18
+
19
+ start_time = Time.now
20
+
21
+ roundtrip_count.times do
22
+ s.send msg, 0
23
+ result = s.recv msg, 0
24
+ raise "Message size doesn't match, expected [#{message_size}] but received [#{msg.size}]" if message_size != msg.size
25
+ end
26
+
27
+ end_time = Time.now
28
+ elapsed_secs = (end_time.to_f - start_time.to_f)
29
+ elapsed_usecs = elapsed_secs * 1000000
30
+ latency = elapsed_usecs / roundtrip_count / 2
31
+
32
+ puts "message size: %i [B]" % message_size
33
+ puts "roundtrip count: %i" % roundtrip_count
34
+ puts "throughput (msgs/s): %i" % (roundtrip_count / elapsed_secs)
35
+ puts "mean latency: %.3f [us]" % latency
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+
5
+ link = "tcp://127.0.0.1:5555"
6
+
7
+ ctx = ZMQ::Context.new 1
8
+ s1 = ctx.socket ZMQ::REQ
9
+ s2 = ctx.socket ZMQ::REP
10
+
11
+ s1.connect link
12
+ s2.bind link
13
+
14
+ poller = ZMQ::Poller.new
15
+ poller.register_readable s2
16
+ poller.register_writable s1
17
+
18
+ start_time = Time.now
19
+ @unsent = true
20
+
21
+ until @done do
22
+ begin
23
+ poller.poll_nonblock
24
+ rescue ZMQ::PollError => e
25
+ puts "efault? [#{e.efault?}]"
26
+ raise
27
+ end
28
+
29
+ # send the message after 5 seconds
30
+ if Time.now - start_time > 5 && @unsent
31
+ payload = "#{ '3' * 1024 }"
32
+
33
+ puts "sending payload nonblocking"
34
+ s1.send_string payload, ZMQ::NOBLOCK
35
+ @unsent = false
36
+ end
37
+
38
+ # check for messages after 1 second
39
+ if Time.now - start_time > 1
40
+ poller.readables.each do |sock|
41
+ received_msg = sock.recv_string ZMQ::NOBLOCK
42
+
43
+ puts "message received [#{received_msg}]"
44
+ @done = true
45
+ end
46
+ end
47
+ end
48
+
49
+ puts "executed in [#{Time.now - start_time}] seconds"
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'ffi-rzmq'
3
+
4
+
5
+ link = "tcp://127.0.0.1:5555"
6
+
7
+ ctx = ZMQ::Context.new 1
8
+ s1 = ctx.socket ZMQ::REQ
9
+ s2 = ctx.socket ZMQ::REP
10
+
11
+ s2.bind link
12
+ s1.connect link
13
+
14
+ payload = "#{ '3' * 2048 }"
15
+ sent_msg = ZMQ::Message.new payload
16
+ received_msg = ZMQ::Message.new
17
+
18
+ s1.send sent_msg
19
+ s2.recv received_msg
20
+
21
+ result = payload == received_msg.copy_out_string ? "Request received" : "Received wrong payload"
22
+
23
+ p result
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ffi-rzmq}
5
+ s.version = "0.5.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Chuck Remes"]
9
+ s.date = %q{2010-06-06}
10
+ s.description = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign
11
+ function interface). It's a pure ruby wrapper so this gem can be loaded
12
+ and run by any ruby runtime that supports FFI. Right now that means
13
+ MRI 1.8.7, 1.9.1 and JRuby.
14
+
15
+ The impetus behind this library was to provide support for ZeroMQ in
16
+ JRuby which has native threads. Unlike MRI, MacRuby, IronRuby and
17
+ Rubinius which all have a GIL, JRuby allows for threaded access to ruby
18
+ code from outside extensions. ZeroMQ is heavily threaded, so until the
19
+ other runtimes remove their GIL, JRuby will likely be the best
20
+ environment to run this library.}
21
+ s.email = %q{cremes@mac.com}
22
+ s.extra_rdoc_files = ["History.txt", "README.rdoc", "version.txt"]
23
+ s.files = ["History.txt", "README.rdoc", "Rakefile", "examples/local_lat.rb", "examples/local_lat_zerocopy.rb", "examples/publish_subscribe.rb", "examples/remote_lat.rb", "examples/remote_lat_zerocopy.rb", "examples/reqrep_poll.rb", "examples/request_response.rb", "ffi-rzmq.gemspec", "lib/ffi-rzmq.rb", "lib/ffi-rzmq/context.rb", "lib/ffi-rzmq/exceptions.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/wrapper.rb", "lib/ffi-rzmq/zmq.rb", "spec/context_spec.rb", "spec/reqrep_spec.rb", "spec/socket_spec.rb", "spec/spec_helper.rb", "version.txt"]
24
+ s.homepage = %q{http://github.com/chuckremes/ffi-rzmq}
25
+ s.rdoc_options = ["--main", "README.rdoc"]
26
+ s.require_paths = ["lib"]
27
+ s.rubyforge_project = %q{ffi-rzmq}
28
+ s.rubygems_version = %q{1.3.6}
29
+ s.summary = %q{This gem wraps the ZeroMQ networking library using the ruby FFI (foreign function interface)}
30
+
31
+ if s.respond_to? :specification_version then
32
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
33
+ s.specification_version = 3
34
+
35
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
36
+ s.add_development_dependency(%q<bones>, [">= 3.4.3"])
37
+ else
38
+ s.add_dependency(%q<bones>, [">= 3.4.3"])
39
+ end
40
+ else
41
+ s.add_dependency(%q<bones>, [">= 3.4.3"])
42
+ end
43
+ end
@@ -0,0 +1,71 @@
1
+
2
+ module ZMQ
3
+
4
+ # :stopdoc:
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ @version ||= File.read(path('version.txt')).strip
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args, &block )
20
+ rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ if block
22
+ begin
23
+ $LOAD_PATH.unshift LIBPATH
24
+ rv = block.call
25
+ ensure
26
+ $LOAD_PATH.shift
27
+ end
28
+ end
29
+ return rv
30
+ end
31
+
32
+ # Returns the lpath for the module. If any arguments are given,
33
+ # they will be joined to the end of the path using
34
+ # <tt>File.join</tt>.
35
+ #
36
+ def self.path( *args, &block )
37
+ rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
38
+ if block
39
+ begin
40
+ $LOAD_PATH.unshift PATH
41
+ rv = block.call
42
+ ensure
43
+ $LOAD_PATH.shift
44
+ end
45
+ end
46
+ return rv
47
+ end
48
+
49
+ # Utility method used to require all files ending in .rb that lie in the
50
+ # directory below this file that has the same name as the filename passed
51
+ # in. Optionally, a specific _directory_ name can be passed in such that
52
+ # the _filename_ does not have to be equivalent to the directory.
53
+ #
54
+ def self.require_all_libs_relative_to( fname, dir = nil )
55
+ dir ||= ::File.basename(fname, '.*')
56
+ search_me = ::File.expand_path(
57
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
58
+
59
+ Dir.glob(search_me).sort.each {|rb| require rb}
60
+ end
61
+
62
+ end # module ZMQ
63
+
64
+ # some code is conditionalized based upon what ruby engine we are
65
+ # executing
66
+ RBX = RUBY_ENGINE =~ /rbx/ ? true : false
67
+
68
+ # the order of files is important
69
+ %w(wrapper zmq exceptions context message socket poll_items poll).each do |file|
70
+ require ZMQ.libpath(['ffi-rzmq', file])
71
+ end
@@ -0,0 +1,99 @@
1
+
2
+ module ZMQ
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
+
9
+ class Context
10
+ include ZMQ::Util
11
+
12
+ attr_reader :context, :pointer
13
+
14
+ # Recommended to just pass 1 for +io_threads+
15
+ # since most programs are not heavily threaded. The rule of thumb
16
+ # is to make +io_threads+ equal to the number of application
17
+ # threads that will be accessing 0mq sockets within this context.
18
+ # The +io_threads+ number specifies the size of the thread pool
19
+ # allocated by 0mq for processing incoming/outgoing messages.
20
+ #
21
+ # Returns a context object. It's necessary for passing to the
22
+ # #Socket constructor when allocating new sockets. All sockets
23
+ # live within a context. Sockets in one context may not be accessed
24
+ # from another context; doing so raises an exception.
25
+ #
26
+ # To connect sockets between contexts, use +inproc+ or +ipc+
27
+ # transport and set up a 0mq socket between them.
28
+ #
29
+ # May raise a #ContextError.
30
+ #
31
+ def initialize io_threads
32
+ @sockets ||= []
33
+ @context = LibZMQ.zmq_init io_threads
34
+ @pointer = @context
35
+ error_check ZMQ_INIT_STR, @context.null? ? 1 : 0
36
+
37
+ define_finalizer
38
+ end
39
+
40
+ # Call to release the context and any remaining data associated
41
+ # with past sockets. This will close any sockets that remain
42
+ # open; further calls to those sockets will raise failure
43
+ # exceptions.
44
+ #
45
+ # Returns nil.
46
+ #
47
+ # May raise a #ContextError.
48
+ #
49
+ def terminate
50
+ unless @context.nil? || @context.null?
51
+ result_code = LibZMQ.zmq_term @context
52
+ error_check ZMQ_TERM_STR, result_code
53
+ @context = nil
54
+ @sockets = nil
55
+ remove_finalizer
56
+ end
57
+ nil
58
+ end
59
+
60
+ # Short-cut to allocate a socket for a specific context.
61
+ #
62
+ # Takes several +type+ values:
63
+ # #ZMQ::REQ
64
+ # #ZMQ::REP
65
+ # #ZMQ::PUB
66
+ # #ZMQ::SUB
67
+ # #ZMQ::PAIR
68
+ # #ZMQ::UPSTREAM
69
+ # #ZMQ::DOWNSTREAM
70
+ # #ZMQ::XREQ
71
+ # #ZMQ::XREP
72
+ #
73
+ # Returns a #ZMQ::Socket.
74
+ #
75
+ # May raise a #ContextError or #SocketError.
76
+ #
77
+ def socket type
78
+ sock = Socket.new @context, type
79
+ error_check ZMQ_SOCKET_STR, sock.nil? ? 1 : 0
80
+ sock
81
+ end
82
+
83
+
84
+ private
85
+
86
+ def define_finalizer
87
+ ObjectSpace.define_finalizer(self, self.class.close(@context))
88
+ end
89
+
90
+ def remove_finalizer
91
+ ObjectSpace.undefine_finalizer self
92
+ end
93
+
94
+ def self.close context
95
+ Proc.new { LibZMQ.zmq_term context unless context.null? }
96
+ end
97
+ end
98
+
99
+ end # module ZMQ
@@ -0,0 +1,145 @@
1
+
2
+ module ZMQ
3
+
4
+ class ZeroMQError < StandardError
5
+ attr_reader :source, :result_code, :error_code, :message
6
+
7
+ def initialize source, result_code, error_code, message
8
+ @source = source
9
+ @result_code = result_code
10
+ @error_code = error_code
11
+ @message = message
12
+ super message
13
+ end
14
+ end # call ZeroMQError
15
+
16
+
17
+ class ContextError < ZeroMQError
18
+ # True when the exception was raised due to the library
19
+ # returning EINVAL.
20
+ #
21
+ # Occurs when he number of app_threads requested is less
22
+ # than one, or the number of io_threads requested is
23
+ # negative.
24
+ #
25
+ def einval?() EINVAL == @error_code; end
26
+
27
+ # True when the exception was raised due to the library
28
+ # returning ETERM.
29
+ #
30
+ # The associated context was terminated.
31
+ #
32
+ def eterm?() ETERM == @error_code; end
33
+
34
+ end # class ContextError
35
+
36
+
37
+ class PollError < ZeroMQError
38
+ # True when the exception was raised due to the library
39
+ # returning EMTHREAD.
40
+ #
41
+ # At least one of the members of the items array refers
42
+ # to a socket belonging to a different application
43
+ # thread.
44
+ #
45
+ def efault?() EFAULT == @error_code; end
46
+
47
+ end # class PollError
48
+
49
+
50
+ class SocketError < ZeroMQError
51
+ # True when the exception was raised due to the library
52
+ # returning EMTHREAD.
53
+ #
54
+ # Occurs for #send and #recv operations.
55
+ # * When calling #send, non-blocking mode was requested
56
+ # and the message cannot be queued at the moment.
57
+ # * When calling #recv, non-blocking mode was requested
58
+ # and no messages are available at the moment.
59
+ #
60
+ def egain?() EAGAIN == @error_code; end
61
+
62
+ # True when the exception was raised due to the library
63
+ # returning ENOCOMPATPROTO.
64
+ #
65
+ # The requested transport protocol is not compatible
66
+ # with the socket type.
67
+ #
68
+ def enocompatproto?() ENOCOMPATPROTO == @error_code; end
69
+
70
+ # True when the exception was raised due to the library
71
+ # returning EPROTONOSUPPORT.
72
+ #
73
+ # The requested transport protocol is not supported.
74
+ #
75
+ def eprotonosupport?() EPROTONOSUPPORT == @error_code; end
76
+
77
+ # True when the exception was raised due to the library
78
+ # returning EADDRINUSE.
79
+ #
80
+ # The given address is already in use.
81
+ #
82
+ def eaddrinuse?() EADDRINUSE == @error_code; end
83
+
84
+ # True when the exception was raised due to the library
85
+ # returning EADDRNOTAVAIL.
86
+ #
87
+ # A nonexistent interface was requested or the
88
+ # requested address was not local.
89
+ #
90
+ def eaddrnotavail?() EADDRNOTAVAIL == @error_code; end
91
+
92
+ # True when the exception was raised due to the library
93
+ # returning EMTHREAD.
94
+ #
95
+ # Occurs under 2 conditions.
96
+ # * When creating a new #Socket, the requested socket
97
+ # type is invalid.
98
+ #
99
+ # * When setting socket options with #setsockopt, the
100
+ # requested option +option_name+ is unknown, or the
101
+ # requested +option_len+ or +option_value+ is invalid.
102
+ #
103
+ def einval?() EINVAL == @error_code; end
104
+
105
+ # True when the exception was raised due to the library
106
+ # returning EMTHREAD.
107
+ #
108
+ # The send or recv operation cannot be performed on this
109
+ # socket at the moment due to the socket not being in
110
+ # the appropriate state. This error may occur with socket
111
+ # types that switch between several states, such as ZMQ::REP.
112
+ #
113
+ def efsm?() EFSM == @error_code; end
114
+
115
+ # True when the exception was raised due to the library
116
+ # returning ENOTSUP.
117
+ #
118
+ # The send or recv operation is not supported by this socket
119
+ # type.
120
+ #
121
+ def enotsup?() super; end
122
+
123
+ # True when the exception was raised due to the library
124
+ # returning EMTHREAD.
125
+ #
126
+ # The number of application threads using sockets within
127
+ # this context has been exceeded. See the +app_threads+
128
+ # parameter of #Context.
129
+ #
130
+ def emthread?() EMTHREAD == @error_code; end
131
+
132
+ end # class SocketError
133
+
134
+
135
+ class MessageError < ZeroMQError
136
+ # True when the exception was raised due to the library
137
+ # returning ENOMEM.
138
+ #
139
+ # Only ever raised by the #Message class when it fails
140
+ # to allocate sufficient memory to send a message.
141
+ #
142
+ def enomem?() ENOMEM == @error_code; end
143
+ end
144
+
145
+ end # module ZMQ