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,101 @@
|
|
1
|
+
|
2
|
+
module ZMQ
|
3
|
+
|
4
|
+
# These methods don't belong to any specific class. They get included
|
5
|
+
# in the #Context, #Socket and #Poller classes.
|
6
|
+
#
|
7
|
+
module Util
|
8
|
+
|
9
|
+
# Returns true when +rc+ is greater than or equal to 0, false otherwise.
|
10
|
+
#
|
11
|
+
# We use the >= test because zmq_poll() returns the number of sockets
|
12
|
+
# that had a read or write event triggered. So, a >= 0 result means
|
13
|
+
# it succeeded.
|
14
|
+
#
|
15
|
+
def self.resultcode_ok? rc
|
16
|
+
rc >= 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the +errno+ as set by the libzmq library.
|
20
|
+
#
|
21
|
+
def self.errno
|
22
|
+
LibZMQ.zmq_errno
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a string corresponding to the currently set #errno. These
|
26
|
+
# error strings are defined by libzmq.
|
27
|
+
#
|
28
|
+
def self.error_string
|
29
|
+
LibZMQ.zmq_strerror(errno).read_string
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an array of the form [major, minor, patch] to represent the
|
33
|
+
# version of libzmq.
|
34
|
+
#
|
35
|
+
# Class method! Invoke as: ZMQ::Util.version
|
36
|
+
#
|
37
|
+
def self.version
|
38
|
+
major = FFI::MemoryPointer.new :int
|
39
|
+
minor = FFI::MemoryPointer.new :int
|
40
|
+
patch = FFI::MemoryPointer.new :int
|
41
|
+
LibZMQ.zmq_version major, minor, patch
|
42
|
+
[major.read_int, minor.read_int, patch.read_int]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the proper flag value for non-blocking regardless of 0mq
|
46
|
+
# version.
|
47
|
+
#
|
48
|
+
if LibZMQ.version2?
|
49
|
+
|
50
|
+
def self.nonblocking_flag
|
51
|
+
NOBLOCK
|
52
|
+
end
|
53
|
+
|
54
|
+
elsif LibZMQ.version3? || LibZMQ.version4?
|
55
|
+
|
56
|
+
def self.nonblocking_flag
|
57
|
+
DONTWAIT
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# :doc:
|
66
|
+
# Called by most library methods to verify there were no errors during
|
67
|
+
# operation. If any are found, raise the appropriate #ZeroMQError.
|
68
|
+
#
|
69
|
+
# When no error is found, this method returns +true+ which is behavior
|
70
|
+
# used internally by #send and #recv.
|
71
|
+
#
|
72
|
+
def error_check source, result_code
|
73
|
+
if -1 == result_code
|
74
|
+
raise_error source, result_code
|
75
|
+
end
|
76
|
+
|
77
|
+
# used by Socket::send/recv, ignored by others
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
def raise_error source, result_code
|
82
|
+
if 'zmq_init' == source || 'zmq_socket' == source
|
83
|
+
raise ContextError.new source, result_code, ZMQ::Util.errno, ZMQ::Util.error_string
|
84
|
+
|
85
|
+
elsif ['zmq_msg_init', 'zmq_msg_init_data', 'zmq_msg_copy', 'zmq_msg_move'].include?(source)
|
86
|
+
raise MessageError.new source, result_code, ZMQ::Util.errno, ZMQ::Util.error_string
|
87
|
+
|
88
|
+
else
|
89
|
+
puts "else"
|
90
|
+
raise ZeroMQError.new source, result_code, -1,
|
91
|
+
"Source [#{source}] does not match any zmq_* strings, rc [#{result_code}], errno [#{ZMQ::Util.errno}], error_string [#{ZMQ::Util.error_string}]"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def eagain?
|
96
|
+
EAGAIN == ZMQ::Util.errno
|
97
|
+
end
|
98
|
+
|
99
|
+
end # module Util
|
100
|
+
|
101
|
+
end # module ZMQ
|
data/spec/context_spec.rb
CHANGED
@@ -7,10 +7,49 @@ module ZMQ
|
|
7
7
|
|
8
8
|
describe Context do
|
9
9
|
|
10
|
-
context "when initializing" do
|
10
|
+
context "when initializing with factory method #create" do
|
11
|
+
include APIHelper
|
12
|
+
|
13
|
+
it "should return nil for negative io threads" do
|
14
|
+
LibZMQ.stub(:zmq_init => nil)
|
15
|
+
Context.create(-1).should be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should default to requesting 1 i/o thread when no argument is passed" do
|
19
|
+
ctx = mock('ctx')
|
20
|
+
ctx.stub!(:null? => false)
|
21
|
+
LibZMQ.should_receive(:zmq_init).with(1).and_return(ctx)
|
22
|
+
|
23
|
+
Context.create
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set the :pointer accessor to non-nil" do
|
27
|
+
ctx = Context.create
|
28
|
+
ctx.pointer.should_not be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set the :context accessor to non-nil" do
|
32
|
+
ctx = Context.create
|
33
|
+
ctx.context.should_not be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should set the :pointer and :context accessors to the same value" do
|
37
|
+
ctx = Context.create
|
38
|
+
ctx.pointer.should == ctx.context
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should define a finalizer on this object" do
|
42
|
+
ObjectSpace.should_receive(:define_finalizer)
|
43
|
+
ctx = Context.create
|
44
|
+
end
|
45
|
+
end # context initializing
|
46
|
+
|
47
|
+
|
48
|
+
context "when initializing with #new" do
|
11
49
|
include APIHelper
|
12
50
|
|
13
51
|
it "should raise an error for negative io threads" do
|
52
|
+
LibZMQ.stub(:zmq_init => nil)
|
14
53
|
lambda { Context.new(-1) }.should raise_exception(ZMQ::ContextError)
|
15
54
|
end
|
16
55
|
|
@@ -23,17 +62,17 @@ module ZMQ
|
|
23
62
|
end
|
24
63
|
|
25
64
|
it "should set the :pointer accessor to non-nil" do
|
26
|
-
ctx =
|
65
|
+
ctx = Context.new
|
27
66
|
ctx.pointer.should_not be_nil
|
28
67
|
end
|
29
68
|
|
30
69
|
it "should set the :context accessor to non-nil" do
|
31
|
-
ctx =
|
70
|
+
ctx = Context.new
|
32
71
|
ctx.context.should_not be_nil
|
33
72
|
end
|
34
73
|
|
35
74
|
it "should set the :pointer and :context accessors to the same value" do
|
36
|
-
ctx =
|
75
|
+
ctx = Context.new
|
37
76
|
ctx.pointer.should == ctx.context
|
38
77
|
end
|
39
78
|
|
@@ -50,27 +89,14 @@ module ZMQ
|
|
50
89
|
LibZMQ.should_receive(:zmq_term).with(ctx.pointer).and_return(0)
|
51
90
|
ctx.terminate
|
52
91
|
end
|
53
|
-
|
54
|
-
it "should raise a ZMQ::ContextError exception when it fails" do
|
55
|
-
ctx = Context.new # can't use a shared context here because we are terminating it!
|
56
|
-
LibZMQ.stub(:zmq_term => 1)
|
57
|
-
lambda { ctx.terminate }.should raise_error(ZMQ::ContextError)
|
58
|
-
end
|
59
92
|
end # context terminate
|
60
93
|
|
61
94
|
|
62
95
|
context "when allocating a socket" do
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# # need to do something with the socket so it's in a state that can return ETERM
|
68
|
-
# end
|
69
|
-
|
70
|
-
it "should raise a ZMQ::SocketError exception when allocation fails" do
|
71
|
-
ctx = spec_ctx
|
72
|
-
Socket.stub(:new => nil)
|
73
|
-
lambda { ctx.socket(ZMQ::REQ) }.should raise_error(ZMQ::SocketError)
|
96
|
+
it "should return nil when allocation fails" do
|
97
|
+
ctx = Context.new
|
98
|
+
LibZMQ.stub!(:zmq_socket => nil)
|
99
|
+
ctx.socket(ZMQ::REQ).should be_nil
|
74
100
|
end
|
75
101
|
end # context socket
|
76
102
|
|
data/spec/device_spec.rb
CHANGED
@@ -1,69 +1,89 @@
|
|
1
1
|
|
2
2
|
require File.join(File.dirname(__FILE__), %w[spec_helper])
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
back = SPEC_CTX.socket(ZMQ::PULL)
|
13
|
-
back.bind(@back_addr)
|
14
|
-
front = SPEC_CTX.socket(ZMQ::PUSH)
|
15
|
-
front.bind(@front_addr)
|
16
|
-
Device.new(ZMQ::STREAMER, back, front)
|
4
|
+
if version2?
|
5
|
+
|
6
|
+
module ZMQ
|
7
|
+
describe Device do
|
8
|
+
include APIHelper
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@ctx = Context.new
|
17
12
|
end
|
18
|
-
sleep 0.5
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should create a streamer device without error given valid opts" do
|
22
|
-
create_streamer
|
23
|
-
end
|
24
13
|
|
25
|
-
|
26
|
-
|
14
|
+
after(:each) do
|
15
|
+
@ctx.terminate
|
16
|
+
end
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
18
|
+
def create_streamer
|
19
|
+
@backport = @frontport = nil
|
20
|
+
Thread.new do
|
21
|
+
back = @ctx.socket(ZMQ::PULL)
|
22
|
+
@backport = bind_to_random_tcp_port(back)
|
23
|
+
front = @ctx.socket(ZMQ::PUSH)
|
24
|
+
@frontport = bind_to_random_tcp_port(front)
|
25
|
+
Device.new(ZMQ::STREAMER, back, front)
|
26
|
+
back.close
|
27
|
+
front.close
|
28
|
+
end
|
29
|
+
sleep 0.5
|
30
|
+
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
res = puller.recv_string(ZMQ::NOBLOCK)
|
36
|
-
res.should == "hello"
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should raise an ArgumentError when trying to pass non-socket objects into the device" do
|
40
|
-
lambda {
|
41
|
-
Device.new(ZMQ::STREAMER, 1,2)
|
42
|
-
}.should raise_exception(ArgumentError)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "should be able to create a forwarder device without error" do
|
46
|
-
back_addr = "tcp://127.0.0.1:#{random_port}"
|
47
|
-
front_addr = "tcp://127.0.0.1:#{random_port}"
|
48
|
-
Thread.new do
|
49
|
-
back = SPEC_CTX.socket(ZMQ::SUB)
|
50
|
-
back.bind(back_addr)
|
51
|
-
front = SPEC_CTX.socket(ZMQ::PUB)
|
52
|
-
front.bind(front_addr)
|
53
|
-
Device.new(ZMQ::FORWARDER, back, front)
|
32
|
+
it "should create a streamer device without error given valid opts" do
|
33
|
+
create_streamer
|
54
34
|
end
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
35
|
+
|
36
|
+
it "should be able to send messages through the device" do
|
37
|
+
create_streamer
|
38
|
+
|
39
|
+
pusher = @ctx.socket(ZMQ::PUSH)
|
40
|
+
pusher.connect("tcp://127.0.0.1:#{@backport}")
|
41
|
+
puller = @ctx.socket(ZMQ::PULL)
|
42
|
+
puller.connect("tcp://127.0.0.1:#{@frontport}")
|
43
|
+
|
44
|
+
pusher.send_string("hello")
|
45
|
+
sleep 0.5
|
46
|
+
res = ''
|
47
|
+
rc = puller.recv_string(res, ZMQ::NOBLOCK)
|
48
|
+
res.should == "hello"
|
49
|
+
|
50
|
+
pusher.close
|
51
|
+
puller.close
|
52
|
+
sleep 0.5
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should raise an ArgumentError when trying to pass non-socket objects into the device" do
|
56
|
+
lambda {
|
57
|
+
Device.new(ZMQ::STREAMER, 1,2)
|
58
|
+
}.should raise_exception(ArgumentError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be able to create a forwarder device without error" do
|
62
|
+
Thread.new do
|
63
|
+
back = @ctx.socket(ZMQ::SUB)
|
64
|
+
bind_to_random_tcp_port(back)
|
65
|
+
front = @ctx.socket(ZMQ::PUB)
|
66
|
+
bind_to_random_tcp_port(front)
|
67
|
+
Device.new(ZMQ::FORWARDER, back, front)
|
68
|
+
back.close
|
69
|
+
front.close
|
70
|
+
end
|
71
|
+
sleep 0.5
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should be able to create a queue device without error" do
|
75
|
+
Thread.new do
|
76
|
+
back = @ctx.socket(ZMQ::ROUTER)
|
77
|
+
bind_to_random_tcp_port(back)
|
78
|
+
front = @ctx.socket(ZMQ::DEALER)
|
79
|
+
bind_to_random_tcp_port(front)
|
80
|
+
Device.new(ZMQ::QUEUE, back, front)
|
81
|
+
back.close
|
82
|
+
front.close
|
83
|
+
end
|
84
|
+
sleep 0.5
|
66
85
|
end
|
67
86
|
end
|
68
87
|
end
|
69
|
-
|
88
|
+
|
89
|
+
end # if version2?
|
data/spec/message_spec.rb
CHANGED
@@ -7,36 +7,114 @@ module ZMQ
|
|
7
7
|
describe Message do
|
8
8
|
|
9
9
|
context "when initializing with an argument" do
|
10
|
-
|
10
|
+
|
11
|
+
it "calls zmq_msg_init_data()" do
|
12
|
+
LibZMQ.should_receive(:zmq_msg_init_data)
|
13
|
+
message = Message.new "text"
|
14
|
+
end
|
15
|
+
|
11
16
|
it "should *not* define a finalizer on this object" do
|
12
17
|
ObjectSpace.should_not_receive(:define_finalizer)
|
13
18
|
Message.new "text"
|
14
19
|
end
|
15
|
-
end # context initializing
|
20
|
+
end # context initializing with arg
|
21
|
+
|
22
|
+
context "when initializing *without* an argument" do
|
16
23
|
|
24
|
+
it "calls zmq_msg_init()" do
|
25
|
+
LibZMQ.should_receive(:zmq_msg_init).and_return(0)
|
26
|
+
message = Message.new
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should *not* define a finalizer on this object" do
|
30
|
+
ObjectSpace.should_not_receive(:define_finalizer)
|
31
|
+
Message.new "text"
|
32
|
+
end
|
33
|
+
end # context initializing with arg
|
17
34
|
|
18
|
-
|
19
|
-
|
35
|
+
|
36
|
+
context "#copy_in_string" do
|
37
|
+
it "calls zmq_msg_init_data()" do
|
20
38
|
message = Message.new "text"
|
21
|
-
|
39
|
+
|
40
|
+
LibZMQ.should_receive(:zmq_msg_init_data)
|
41
|
+
message.copy_in_string("new text")
|
22
42
|
end
|
23
|
-
|
24
|
-
|
25
|
-
context "when copying binary data" do
|
26
|
-
it "should find the correct length by ignoring encoding" do
|
43
|
+
|
44
|
+
it "correctly finds the length of binary data by ignoring encoding" do
|
27
45
|
message = Message.new
|
28
46
|
message.copy_in_string("\x83\x6e\x04\x00\x00\x44\xd1\x81")
|
29
47
|
message.size.should == 8
|
30
48
|
end
|
31
49
|
end
|
32
50
|
|
51
|
+
|
52
|
+
context "#copy" do
|
53
|
+
it "calls zmq_msg_copy()" do
|
54
|
+
message = Message.new "text"
|
55
|
+
copy = Message.new
|
56
|
+
|
57
|
+
LibZMQ.should_receive(:zmq_msg_copy)
|
58
|
+
copy.copy(message)
|
59
|
+
end
|
60
|
+
end # context copy
|
61
|
+
|
62
|
+
|
63
|
+
context "#move" do
|
64
|
+
it "calls zmq_msg_move()" do
|
65
|
+
message = Message.new "text"
|
66
|
+
copy = Message.new
|
67
|
+
|
68
|
+
LibZMQ.should_receive(:zmq_msg_move)
|
69
|
+
copy.move(message)
|
70
|
+
end
|
71
|
+
end # context move
|
72
|
+
|
73
|
+
|
74
|
+
context "#size" do
|
75
|
+
it "calls zmq_msg_size()" do
|
76
|
+
message = Message.new "text"
|
77
|
+
|
78
|
+
LibZMQ.should_receive(:zmq_msg_size)
|
79
|
+
message.size
|
80
|
+
end
|
81
|
+
end # context size
|
82
|
+
|
83
|
+
|
84
|
+
context "#data" do
|
85
|
+
it "calls zmq_msg_data()" do
|
86
|
+
message = Message.new "text"
|
87
|
+
|
88
|
+
LibZMQ.should_receive(:zmq_msg_data)
|
89
|
+
message.data
|
90
|
+
end
|
91
|
+
end # context data
|
92
|
+
|
93
|
+
|
94
|
+
context "#close" do
|
95
|
+
it "calls zmq_msg_close() the first time" do
|
96
|
+
message = Message.new "text"
|
97
|
+
|
98
|
+
LibZMQ.should_receive(:zmq_msg_close)
|
99
|
+
message.close
|
100
|
+
end
|
101
|
+
|
102
|
+
it "*does not* call zmq_msg_close() on subsequent invocations" do
|
103
|
+
message = Message.new "text"
|
104
|
+
message.close
|
105
|
+
|
106
|
+
LibZMQ.should_not_receive(:zmq_msg_close)
|
107
|
+
message.close
|
108
|
+
end
|
109
|
+
end # context close
|
110
|
+
|
33
111
|
end # describe Message
|
34
|
-
|
35
|
-
|
112
|
+
|
113
|
+
|
36
114
|
describe ManagedMessage do
|
37
115
|
|
38
116
|
context "when initializing with an argument" do
|
39
|
-
|
117
|
+
|
40
118
|
it "should define a finalizer on this object" do
|
41
119
|
ObjectSpace.should_receive(:define_finalizer)
|
42
120
|
ManagedMessage.new "text"
|