ffi-rzmq 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/AUTHORS.txt +1 -0
  2. data/History.txt +35 -0
  3. data/README.rdoc +48 -15
  4. data/Rakefile +7 -2
  5. data/examples/README.rdoc +21 -76
  6. data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
  7. data/examples/v2api/local_lat_poll.rb +66 -0
  8. data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
  9. data/examples/v2api/publish_subscribe.rb +82 -0
  10. data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
  11. data/examples/v2api/remote_throughput.rb +39 -0
  12. data/examples/v2api/reqrep_poll.rb +62 -0
  13. data/examples/v2api/request_response.rb +40 -0
  14. data/examples/v2api/throughput_measurement.rb +138 -0
  15. data/examples/v3api/local_lat.rb +59 -0
  16. data/examples/v3api/local_lat_poll.rb +66 -0
  17. data/examples/v3api/local_throughput.rb +65 -0
  18. data/examples/v3api/publish_subscribe.rb +82 -0
  19. data/examples/v3api/remote_lat.rb +71 -0
  20. data/examples/v3api/remote_throughput.rb +47 -0
  21. data/examples/v3api/reqrep_poll.rb +62 -0
  22. data/examples/v3api/request_response.rb +40 -0
  23. data/examples/v3api/throughput_measurement.rb +166 -0
  24. data/ext/README +5 -0
  25. data/ffi-rzmq.gemspec +4 -4
  26. data/lib/ffi-rzmq.rb +4 -1
  27. data/lib/ffi-rzmq/constants.rb +178 -0
  28. data/lib/ffi-rzmq/context.rb +61 -45
  29. data/lib/ffi-rzmq/device.rb +22 -9
  30. data/lib/ffi-rzmq/exceptions.rb +0 -98
  31. data/lib/ffi-rzmq/libc.rb +19 -0
  32. data/lib/ffi-rzmq/libzmq.rb +188 -0
  33. data/lib/ffi-rzmq/message.rb +33 -40
  34. data/lib/ffi-rzmq/poll.rb +49 -52
  35. data/lib/ffi-rzmq/socket.rb +902 -392
  36. data/lib/ffi-rzmq/util.rb +101 -0
  37. data/spec/context_spec.rb +47 -21
  38. data/spec/device_spec.rb +78 -58
  39. data/spec/message_spec.rb +90 -12
  40. data/spec/multipart_spec.rb +162 -0
  41. data/spec/nonblocking_recv_spec.rb +325 -0
  42. data/spec/pushpull_spec.rb +95 -34
  43. data/spec/reqrep_spec.rb +55 -20
  44. data/spec/socket_spec.rb +353 -204
  45. data/spec/spec_helper.rb +46 -3
  46. data/version.txt +1 -1
  47. metadata +91 -66
  48. data/examples/local_lat_poll.rb +0 -54
  49. data/examples/local_lat_zerocopy.rb +0 -24
  50. data/examples/publish_subscribe.rb +0 -52
  51. data/examples/remote_lat_zerocopy.rb +0 -35
  52. data/examples/remote_throughput.rb +0 -27
  53. data/examples/reqrep_poll.rb +0 -49
  54. data/examples/request_response.rb +0 -23
  55. data/lib/ffi-rzmq/wrapper.rb +0 -121
  56. 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
@@ -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 = spec_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 = spec_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 = spec_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
- # it "should return a ZMQ::Socket" do
64
- # ctx = spec_ctx
65
- # sock = ctx.socket ZMQ::REQ
66
- # sock.should be_kind_of(ZMQ::Socket)
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
 
@@ -1,69 +1,89 @@
1
1
 
2
2
  require File.join(File.dirname(__FILE__), %w[spec_helper])
3
3
 
4
- module ZMQ
5
- describe Device do
6
- include APIHelper
7
-
8
- def create_streamer
9
- @back_addr = "tcp://127.0.0.1:#{random_port}"
10
- @front_addr = "tcp://127.0.0.1:#{random_port}"
11
- Thread.new do
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
- it "should be able to send messages through the device" do
26
- create_streamer
14
+ after(:each) do
15
+ @ctx.terminate
16
+ end
27
17
 
28
- pusher = SPEC_CTX.socket(ZMQ::PUSH)
29
- pusher.connect(@back_addr)
30
- puller = SPEC_CTX.socket(ZMQ::PULL)
31
- puller.connect(@front_addr)
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
- pusher.send_string("hello")
34
- sleep 0.5
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
- end
56
-
57
- it "should be able to create a queue device without error" do
58
- back_addr = "tcp://127.0.0.1:#{random_port}"
59
- front_addr = "tcp://127.0.0.1:#{random_port}"
60
- Thread.new do
61
- back = SPEC_CTX.socket(ZMQ::ROUTER)
62
- back.bind(back_addr)
63
- front = SPEC_CTX.socket(ZMQ::DEALER)
64
- front.bind(front_addr)
65
- Device.new(ZMQ::QUEUE, back, front)
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
- end
88
+
89
+ end # if version2?
@@ -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
- context "when copying in data" do
19
- it "should raise a MessageError when the Message is being reused" do
35
+
36
+ context "#copy_in_string" do
37
+ it "calls zmq_msg_init_data()" do
20
38
  message = Message.new "text"
21
- lambda { message.copy_in_string("new text") }.should raise_error(MessageError)
39
+
40
+ LibZMQ.should_receive(:zmq_msg_init_data)
41
+ message.copy_in_string("new text")
22
42
  end
23
- end
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"