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.
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"