nn-core 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,16 @@
1
+ 20130220 / 0.1.6
2
+ * Several fixes for tcp string handling have made it to nanomsg trunk,
3
+ so I can now re-enable those specs.
4
+
5
+ * For MRI Ruby, add @blocking=true before all function attachments to instruct
6
+ the FFI gem to release the GIL.
7
+
8
+ * Fixed a nn_recv spec to only read a specific number of bytes from a received
9
+ string. Must note that the library does not null terminate strings on its
10
+ own so we need to be very careful how many bytes are read from received
11
+ strings.
12
+
13
+
1
14
  20130215 / 0.1.5
2
15
  * libnanomsg has removed the nn_init and nn_term functions. Updated
3
16
  specs to remove those features.
@@ -0,0 +1,167 @@
1
+ begin
2
+ require 'nn-core'
3
+ rescue LoadError
4
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
5
+ retry
6
+ end
7
+
8
+
9
+ # Within a single process, we start up two threads. One thread has a REQ (request)
10
+ # socket and the second thread has a REP (reply) socket. We measure the
11
+ # *round-trip* latency between these sockets. Only *one* message is in flight at
12
+ # any given moment.
13
+ #
14
+ # % ruby roundtrip_latency.rb tcp://127.0.0.1:5555 1024 1_000_000
15
+ #
16
+ # % ruby roundtrip_latency.rb inproc://lm_sock 1024 1_000_000
17
+ #
18
+
19
+ if ARGV.length < 3
20
+ puts "usage: ruby roundtrip_latency.rb <connect-to> <message-size> <roundtrip-count>"
21
+ exit
22
+ end
23
+
24
+ link = ARGV[0]
25
+ message_size = ARGV[1].to_i
26
+ roundtrip_count = ARGV[2].to_i
27
+
28
+ def set_signal_handler(receiver, transmitter)
29
+ trap(:INT) do
30
+ puts "got ctrl-c"
31
+ receiver.terminate
32
+ transmitter.terminate
33
+ end
34
+ end
35
+
36
+ class Node
37
+ def initialize(endpoint, size, count)
38
+ @endpoint = endpoint
39
+ @size = size
40
+ @count = count
41
+ @msg = 'a' * @size
42
+
43
+ @rcv_buffer = FFI::MemoryPointer.new(@size)
44
+
45
+ allocate_socket
46
+ setup_socket
47
+ set_endpoint
48
+ end
49
+
50
+ def setup_socket
51
+ option = FFI::MemoryPointer.new(:int32)
52
+
53
+ # LINGER
54
+ option.write_int(100)
55
+ rc = NNCore::LibNanomsg.nn_setsockopt(@socket, NNCore::NN_SOL_SOCKET, NNCore::NN_LINGER, option, 4)
56
+ assert(rc)
57
+
58
+ # SNDBUF
59
+ option.write_int(131072)
60
+ rc = NNCore::LibNanomsg.nn_setsockopt(@socket, NNCore::NN_SOL_SOCKET, NNCore::NN_SNDBUF, option, 4)
61
+ assert(rc)
62
+
63
+ # RCVBUF
64
+ option.write_int(131072)
65
+ rc = NNCore::LibNanomsg.nn_setsockopt(@socket, NNCore::NN_SOL_SOCKET, NNCore::NN_RCVBUF, option, 4)
66
+ assert(rc)
67
+ end
68
+
69
+ def send_msg
70
+ nbytes = NNCore::LibNanomsg.nn_send(@socket, @msg, @size, 0)
71
+ assert(nbytes)
72
+ nbytes
73
+ end
74
+
75
+ def recv_msg
76
+ nbytes = NNCore::LibNanomsg.nn_recv(@socket, @rcv_buffer, @size, 0)
77
+ assert(nbytes)
78
+ nbytes
79
+ end
80
+
81
+ def terminate
82
+ assert(NNCore::LibNanomsg.nn_close(@socket))
83
+ end
84
+
85
+ def assert(rc)
86
+ raise "Last API call failed at #{caller(1)}" unless rc >= 0
87
+ end
88
+ end
89
+
90
+ class Receiver < Node
91
+ def allocate_socket
92
+ @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REP)
93
+ assert(@socket)
94
+ end
95
+
96
+ def set_endpoint
97
+ assert(NNCore::LibNanomsg.nn_bind(@socket, @endpoint))
98
+ end
99
+
100
+ def run
101
+ @count.times do
102
+ nbytes = recv_msg
103
+
104
+ raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != nbytes
105
+
106
+ send_msg
107
+ end
108
+
109
+ terminate
110
+ end
111
+ end
112
+
113
+ class Transmitter < Node
114
+ def allocate_socket
115
+ @socket = NNCore::LibNanomsg.nn_socket(NNCore::AF_SP, NNCore::NN_REQ)
116
+ assert(@socket)
117
+ end
118
+
119
+ def set_endpoint
120
+ assert(NNCore::LibNanomsg.nn_connect(@socket, @endpoint))
121
+ end
122
+
123
+ def run
124
+ elapsed = elapsed_microseconds do
125
+ @count.times do
126
+ send_msg
127
+
128
+ nbytes = recv_msg
129
+
130
+ raise "Message size doesn't match, expected [#{@size}] but received [#{nbytes}]" if @size != nbytes
131
+ end
132
+ end
133
+
134
+ latency = elapsed / @count / 2
135
+
136
+ puts "message size: %i [B]" % @size
137
+ puts "roundtrip count: %i" % @count
138
+ puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
139
+ puts "mean latency: %.3f [us]" % latency
140
+ terminate
141
+ end
142
+
143
+ def elapsed_microseconds(&blk)
144
+ start = Time.now
145
+ yield
146
+ value = ((Time.now - start) * 1_000_000)
147
+ end
148
+ end
149
+
150
+ threads = []
151
+ receiver = transmitter = nil
152
+
153
+ threads << Thread.new do
154
+ receiver = Receiver.new(link, message_size, roundtrip_count)
155
+ receiver.run
156
+ end
157
+
158
+ sleep 1
159
+
160
+ threads << Thread.new do
161
+ transmitter = Transmitter.new(link, message_size, roundtrip_count)
162
+ transmitter.run
163
+ end
164
+
165
+ set_signal_handler(receiver, transmitter)
166
+
167
+ threads.each {|t| t.join}
@@ -33,25 +33,41 @@ module NNCore
33
33
  # Size_t not working properly on Windows
34
34
  find_type(:size_t) rescue typedef(:ulong, :size_t)
35
35
 
36
+ @blocking = true
36
37
  attach_function :nn_version, [:pointer, :pointer, :pointer], :int
37
38
 
39
+ @blocking = true
38
40
  attach_function :nn_errno, [], :int
41
+ @blocking = true
39
42
  attach_function :nn_strerror, [:int], :string
43
+ @blocking = true
40
44
  attach_function :nn_socket, [:int, :int], :int
45
+ @blocking = true
41
46
  attach_function :nn_close, [:int], :int
42
47
 
48
+ @blocking = true
43
49
  attach_function :nn_getsockopt, [:int, :int, :int, :pointer, :pointer], :int
50
+ @blocking = true
44
51
  attach_function :nn_setsockopt, [:int, :int, :int, :pointer, :size_t], :int
52
+ @blocking = true
45
53
  attach_function :nn_bind, [:int, :string], :int
54
+ @blocking = true
46
55
  attach_function :nn_connect, [:int, :string], :int
56
+ @blocking = true
47
57
  attach_function :nn_shutdown, [:int, :int], :int
58
+ @blocking = true
48
59
  attach_function :nn_send, [:int, :pointer, :size_t, :int], :int
60
+ @blocking = true
49
61
  attach_function :nn_recv, [:int, :pointer, :size_t, :int], :int
50
62
 
51
63
  # functions for working with raw buffers
64
+ @blocking = true
52
65
  attach_function :nn_sendmsg, [:int, :pointer, :int], :int
66
+ @blocking = true
53
67
  attach_function :nn_recvmsg, [:int, :pointer, :int], :int
68
+ @blocking = true
54
69
  attach_function :nn_allocmsg, [:size_t, :int], :pointer
70
+ @blocking = true
55
71
  attach_function :nn_freemsg, [:pointer], :int
56
72
  end
57
73
  end
@@ -1,3 +1,3 @@
1
1
  module NNCore
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -53,35 +53,28 @@ module NNCore
53
53
  LibNanomsg.nn_errno.should == EINVAL
54
54
  end
55
55
 
56
- # it "returns -1 for an invalid TCP address (missing port)" do
57
- # rc = LibNanomsg.nn_connect(@socket, "tcp://*:")
58
- # rc.should == -1
59
- # LibNanomsg.nn_errno.should == EINVAL
60
- # end
61
- #
62
- # it "returns -1 for an invalid TCP address (non-numeric port)" do
63
- # rc = LibNanomsg.nn_connect(@socket, "tcp://192.168.0.1:port")
64
- # rc.should == -1
65
- # LibNanomsg.nn_errno.should == EINVAL
66
- # end
67
- #
68
- # it "returns -1 for an invalid TCP address (port number is out of range)" do
69
- # rc = LibNanomsg.nn_connect(@socket, "tcp://192.168.0.1:65536")
70
- # rc.should == -1
71
- # LibNanomsg.nn_errno.should == EINVAL
72
- # end
56
+ it "returns -1 for an invalid TCP address (missing port)" do
57
+ rc = LibNanomsg.nn_connect(@socket, "tcp://*:")
58
+ rc.should == -1
59
+ LibNanomsg.nn_errno.should == EINVAL
60
+ end
73
61
 
74
- it "returns -1 for an unsupported transport protocol" do
75
- rc = LibNanomsg.nn_connect(@socket, "zmq://192.168.0.1:65536")
62
+ it "returns -1 for an invalid TCP address (non-numeric port)" do
63
+ rc = LibNanomsg.nn_connect(@socket, "tcp://192.168.0.1:port")
76
64
  rc.should == -1
77
- LibNanomsg.nn_errno.should == EPROTONOSUPPORT
65
+ LibNanomsg.nn_errno.should == EINVAL
66
+ end
67
+
68
+ it "returns -1 for an invalid TCP address (port number is out of range)" do
69
+ rc = LibNanomsg.nn_connect(@socket, "tcp://192.168.0.1:65536")
70
+ rc.should == -1
71
+ LibNanomsg.nn_errno.should == EINVAL
78
72
  end
79
73
 
80
- it "returns -1 for specifying a non-existent device using TCP transport" do
81
- pending # currently fails
82
- rc = LibNanomsg.nn_connect(@socket, "tcp://doesntexist:5555")
74
+ it "returns -1 for an unsupported transport protocol" do
75
+ rc = LibNanomsg.nn_connect(@socket, "zmq://192.168.0.1:65536")
83
76
  rc.should == -1
84
- LibNanomsg.nn_errno.should == ENODEV
77
+ LibNanomsg.nn_errno.should == EPROTONOSUPPORT
85
78
  end
86
79
 
87
80
  it "returns 2 when connecting twice to an existing endpoint" do
@@ -40,7 +40,10 @@ module NNCore
40
40
  buffer = FFI::MemoryPointer.new(:pointer)
41
41
  nbytes = LibNanomsg.nn_recv(@socket, buffer, NN_MSG, 0)
42
42
  nbytes.should == 3
43
- buffer.get_pointer(0).read_string.should == string
43
+
44
+ # important to pass +nbytes+ to #read_string since the sent string
45
+ # is not null-terminated
46
+ buffer.get_pointer(0).read_string(nbytes).should == string
44
47
  end
45
48
  end
46
49
  end
@@ -33,11 +33,10 @@ module NNCore
33
33
  end
34
34
 
35
35
  context "given an unsupported address family" do
36
-
37
- # it "nn_socket returns -1 and sets nn_errno to EAFNOSUPPORT" do
38
- # LibNanomsg.nn_socket(0, NN_PUB).should == -1
39
- # LibNanomsg.nn_errno.should == EAFNOSUPPORT
40
- # end
36
+ it "nn_socket returns -1 and sets nn_errno to EAFNOSUPPORT" do
37
+ LibNanomsg.nn_socket(0, NN_PUB).should == -1
38
+ LibNanomsg.nn_errno.should == EAFNOSUPPORT
39
+ end
41
40
  end
42
41
 
43
42
  context "given an unsupported protocol and a supported address family" do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module NNCore
4
- describe "nn_bind" do
4
+ describe "nn_version" do
5
5
 
6
6
  context "given an initialized library" do
7
7
 
@@ -6,7 +6,7 @@ module NNCore
6
6
  :NN_LINGER => NN_LINGER,
7
7
  :NN_SNDBUF => NN_SNDBUF,
8
8
  :NN_RCVBUF => NN_RCVBUF,
9
- :NN_SENDTIMEO => NN_SNDTIMEO,
9
+ :NN_SNDTIMEO => NN_SNDTIMEO,
10
10
  :NN_RCVTIMEO => NN_RCVTIMEO,
11
11
  :NN_RECONNECT_IVL => NN_RECONNECT_IVL,
12
12
  :NN_RECONNECT_IVL_MAX => NN_RECONNECT_IVL_MAX,
metadata CHANGED
@@ -1,64 +1,64 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nn-core
3
3
  version: !ruby/object:Gem::Version
4
+ version: 0.1.6
4
5
  prerelease:
5
- version: 0.1.5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Chuck Remes
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-15 00:00:00.000000000 Z
12
+ date: 2013-02-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- prerelease: false
16
- requirement: !ruby/object:Gem::Requirement
15
+ version_requirements: !ruby/object:Gem::Requirement
17
16
  requirements:
18
17
  - - ! '>='
19
18
  - !ruby/object:Gem::Version
20
19
  version: '0'
21
20
  none: false
22
21
  type: :runtime
23
- version_requirements: !ruby/object:Gem::Requirement
22
+ name: ffi
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
24
25
  requirements:
25
26
  - - ! '>='
26
27
  - !ruby/object:Gem::Version
27
28
  version: '0'
28
29
  none: false
29
- name: ffi
30
30
  - !ruby/object:Gem::Dependency
31
- prerelease: false
32
- requirement: !ruby/object:Gem::Requirement
31
+ version_requirements: !ruby/object:Gem::Requirement
33
32
  requirements:
34
33
  - - ~>
35
34
  - !ruby/object:Gem::Version
36
35
  version: '2.6'
37
36
  none: false
38
37
  type: :development
39
- version_requirements: !ruby/object:Gem::Requirement
38
+ name: rspec
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
40
41
  requirements:
41
42
  - - ~>
42
43
  - !ruby/object:Gem::Version
43
44
  version: '2.6'
44
45
  none: false
45
- name: rspec
46
46
  - !ruby/object:Gem::Dependency
47
- prerelease: false
48
- requirement: !ruby/object:Gem::Requirement
47
+ version_requirements: !ruby/object:Gem::Requirement
49
48
  requirements:
50
49
  - - ! '>='
51
50
  - !ruby/object:Gem::Version
52
51
  version: '0'
53
52
  none: false
54
53
  type: :development
55
- version_requirements: !ruby/object:Gem::Requirement
54
+ name: rake
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
56
57
  requirements:
57
58
  - - ! '>='
58
59
  - !ruby/object:Gem::Version
59
60
  version: '0'
60
61
  none: false
61
- name: rake
62
62
  description: ! 'Wraps the nanomsg networking library using the ruby FFI (foreign
63
63
 
64
64
  function interface). It only exposes the native C API to Ruby. Other gems should
@@ -85,6 +85,8 @@ files:
85
85
  UkVBRE1FLm1k
86
86
  - !binary |-
87
87
  UmFrZWZpbGU=
88
+ - !binary |-
89
+ ZXhhbXBsZXMvcm91bmR0cmlwX2xhdGVuY3kucmI=
88
90
  - !binary |-
89
91
  bGliL25uLWNvcmUucmI=
90
92
  - !binary |-