nn-core 0.1.5 → 0.1.6
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/History.txt +13 -0
- data/examples/roundtrip_latency.rb +167 -0
- data/lib/nn-core/libnanomsg.rb +16 -0
- data/lib/nn-core/version.rb +1 -1
- data/spec/nn_connect_spec.rb +17 -24
- data/spec/nn_recv_spec.rb +4 -1
- data/spec/nn_socket_spec.rb +4 -5
- data/spec/nn_version_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- metadata +16 -14
data/History.txt
CHANGED
@@ -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}
|
data/lib/nn-core/libnanomsg.rb
CHANGED
@@ -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
|
data/lib/nn-core/version.rb
CHANGED
data/spec/nn_connect_spec.rb
CHANGED
@@ -53,35 +53,28 @@ module NNCore
|
|
53
53
|
LibNanomsg.nn_errno.should == EINVAL
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
75
|
-
rc = LibNanomsg.nn_connect(@socket, "
|
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 ==
|
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
|
81
|
-
|
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 ==
|
77
|
+
LibNanomsg.nn_errno.should == EPROTONOSUPPORT
|
85
78
|
end
|
86
79
|
|
87
80
|
it "returns 2 when connecting twice to an existing endpoint" do
|
data/spec/nn_recv_spec.rb
CHANGED
@@ -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
|
-
|
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
|
data/spec/nn_socket_spec.rb
CHANGED
@@ -33,11 +33,10 @@ module NNCore
|
|
33
33
|
end
|
34
34
|
|
35
35
|
context "given an unsupported address family" do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
data/spec/nn_version_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
:
|
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-
|
12
|
+
date: 2013-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 |-
|