nng 0.1.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/LICENSE +22 -0
- data/README.md +308 -0
- data/ext/rbnng/device.c +43 -0
- data/ext/rbnng/exceptions.c +116 -207
- data/ext/rbnng/extconf.rb +6 -19
- data/ext/rbnng/message.c +183 -0
- data/ext/rbnng/pipe.c +81 -0
- data/ext/rbnng/rbnng.c +15 -29
- data/ext/rbnng/rbnng.h +59 -26
- data/ext/rbnng/socket.c +661 -91
- data/ext/rbnng/stats.c +106 -0
- data/ext/rbnng/tls.c +127 -0
- data/lib/nng/socket/base.rb +131 -0
- data/lib/nng/socket/bus0.rb +10 -0
- data/lib/nng/socket/pair0.rb +10 -0
- data/lib/nng/socket/pair1.rb +10 -0
- data/lib/nng/socket/pub0.rb +9 -0
- data/lib/nng/socket/pull0.rb +9 -0
- data/lib/nng/socket/push0.rb +9 -0
- data/lib/nng/socket/readable.rb +17 -0
- data/lib/nng/socket/rep0.rb +10 -0
- data/lib/nng/socket/req0.rb +10 -0
- data/lib/nng/socket/respondent0.rb +10 -0
- data/lib/nng/socket/sub0.rb +9 -0
- data/lib/nng/socket/surveyor0.rb +10 -0
- data/lib/nng/socket/writable.rb +17 -0
- data/lib/nng/tls.rb +25 -0
- data/lib/nng/version.rb +3 -1
- data/lib/nng.rb +24 -1
- metadata +44 -31
- data/ext/rbnng/bus0.c +0 -43
- data/ext/rbnng/exceptions.h +0 -12
- data/ext/rbnng/msg.c +0 -61
- data/ext/rbnng/msg.h +0 -22
- data/ext/rbnng/pair.c +0 -65
- data/ext/rbnng/pub0.c +0 -36
- data/ext/rbnng/pull0.c +0 -36
- data/ext/rbnng/push0.c +0 -36
- data/ext/rbnng/rep0.c +0 -150
- data/ext/rbnng/req0.c +0 -38
- data/ext/rbnng/respondent0.c +0 -40
- data/ext/rbnng/socket.h +0 -36
- data/ext/rbnng/sub0.c +0 -41
- data/ext/rbnng/surveyor0.c +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e49576e9e33fb3e2a0bc98702bdd42c8055aed72390b67cb2378fe428ec1c3f9
|
|
4
|
+
data.tar.gz: d2f7a9e2a8d50eb8bed827f7a5ec1dfbcddc96a7cb9a7d74761b1c4309a9f9ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9925501abf685c5aec0b4f25b7d274635805dfe4597d34ce0caaaad7355a0a88556632060f265754af5a7f8f3015428adce090acabf47fd2fc6240082380ee4d
|
|
7
|
+
data.tar.gz: c0741f1c0e049eeefc1c4c2c9b26c57305a42cd2136b6665521e20d2fed99ab7e27a2b2c9359f8c65fb2adc5cd0d37a8313c321d7a3dee7cc7e28248305a9ffd
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- Rewritten C extension from scratch
|
|
8
|
+
- `raw` argument changed from positional bool to keyword (`raw: true`)
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **TLS transport** — `tls+tcp://` URLs with `cert:`, `key:`, `ca:`, `verify:`, `server_name:` kwargs on `#listen` and `#dial`
|
|
13
|
+
- **Mutual TLS** — server-side client certificate verification
|
|
14
|
+
- **`NNG::Pipe`** — lightweight pipe introspection via `Message#pipe`
|
|
15
|
+
- `#tls_verified?` — whether peer certificate was verified
|
|
16
|
+
- `#tls_peer_cn` — peer certificate common name
|
|
17
|
+
- `#id` — pipe identifier
|
|
18
|
+
- **`NNG::Device`** — transparent message forwarding between sockets
|
|
19
|
+
- **Pipe event notifications** — `Socket::Base#on_pipe_event` yields `:connect`/`:disconnect` events with `NNG::Pipe`
|
|
20
|
+
- **Runtime statistics** — `NNG.stats` and `NNG.stats_for(socket)` return NNG stat snapshots as nested Hashes
|
|
21
|
+
- **`Sub0#subscribe` / `Sub0#unsubscribe`** — add/remove topic subscriptions at runtime
|
|
22
|
+
- `Socket::Base#forward(msg)` — send an existing message preserving its header, enabling stateless raw mode proxying
|
|
23
|
+
- `Socket::Base#raw?` — check whether a socket was opened in raw mode
|
|
24
|
+
- `Sub0.new(prefix:)` — subscribe to a topic prefix at construction time
|
|
25
|
+
- Socket option accessors:
|
|
26
|
+
- `name` / `name=` — socket name
|
|
27
|
+
- `urls` — list of listen/dial URLs
|
|
28
|
+
- `recv_buffer` / `recv_buffer=` — receive buffer size
|
|
29
|
+
- `send_buffer` / `send_buffer=` — send buffer size
|
|
30
|
+
- `recv_max_size` / `recv_max_size=` — max receive message size
|
|
31
|
+
- `recv_timeout` / `recv_timeout=` — receive timeout (ms)
|
|
32
|
+
- `send_timeout` / `send_timeout=` — send timeout (ms)
|
|
33
|
+
- `reconnect_time` / `reconnect_time=` — reconnect interval as a `Range` of seconds
|
|
34
|
+
- `protocol_name` / `peer_name` — read-only protocol info
|
|
35
|
+
- Generic option accessors: `get_opt_int`, `set_opt_int`, `get_opt_ms`, `set_opt_ms`, `get_opt_size`, `set_opt_size`, `get_opt_string`, `set_opt_string`
|
|
36
|
+
- `wait_readable` / `wait_writable` default to the socket's recv/send timeout
|
|
37
|
+
- `receive` / `send` raise `Timeout::Error` on timeout
|
|
38
|
+
- `NNG.nng_version` — returns nng library version as a 3-element array
|
|
39
|
+
- Message manipulation: `#body`, `#body_clear`, `#body_append`, `#header`, `#header=`, `#dup`, `#consumed?`
|
|
40
|
+
- pkg-config support in `extconf.rb` with fallback to system library path
|
|
41
|
+
- ZGuide messaging pattern examples (pipeline, lazy pirate, pub/sub, heartbeat, LVC, bstar, clone)
|
|
42
|
+
- Comprehensive specs for all protocols, TLS, raw mode, timeouts, memory management, and socket options
|
|
43
|
+
- Benchmarks for throughput and latency (inproc/IPC/TCP, async/threads)
|
|
44
|
+
|
|
45
|
+
## 0.1.0
|
|
46
|
+
|
|
47
|
+
- Initial release with pair0/pair1, req0/rep0, pub0/sub0, push0/pull0, bus0, surveyor0/respondent0
|
|
48
|
+
- C extension using nng FFI bindings
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Adib Saad, Patrik Wenger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# rbnng
|
|
2
|
+
|
|
3
|
+
[](https://github.com/paddor/rbnng/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/nng)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](#tls)
|
|
7
|
+
[](https://www.ruby-lang.org)
|
|
8
|
+
|
|
9
|
+
Fast, native Ruby bindings for [nng](https://nng.nanomsg.org/) — a lightweight, broker-less messaging library for building distributed systems.
|
|
10
|
+
|
|
11
|
+
> **47k+ msg/s** inproc throughput | **57 µs** fiber roundtrip latency | TLS built-in
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Highlights
|
|
16
|
+
|
|
17
|
+
- **All scalability protocols** — req/rep, pub/sub, push/pull, pair, survey, bus
|
|
18
|
+
- **Native C extension** — no FFI overhead, GVL released during blocking I/O, thread-safe
|
|
19
|
+
- **Async-first** — first-class [async](https://github.com/socketry/async) fiber support
|
|
20
|
+
- **Zero-copy forwarding** — `#forward` transfers message ownership without copying
|
|
21
|
+
- **Raw mode** — bypass the protocol state machine for scalable proxies
|
|
22
|
+
- **TLS transport** — `tls+tcp://` with mTLS, certificate pinning, and peer introspection
|
|
23
|
+
- **Nonblock-first optimization** — tries `NNG_FLAG_NONBLOCK` before releasing the GVL for up to 26% higher throughput
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
Install nng on your system:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
# macOS
|
|
31
|
+
brew install nng
|
|
32
|
+
|
|
33
|
+
# Debian/Ubuntu
|
|
34
|
+
apt install libnng-dev
|
|
35
|
+
|
|
36
|
+
# Arch
|
|
37
|
+
pacman -S nng
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then add the gem:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
gem install nng
|
|
44
|
+
# or in Gemfile
|
|
45
|
+
gem 'nng'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary>TLS support (optional)</summary>
|
|
50
|
+
|
|
51
|
+
Install mbedTLS to enable the `tls+tcp://` transport:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
# macOS
|
|
55
|
+
brew install mbedtls
|
|
56
|
+
|
|
57
|
+
# Debian/Ubuntu
|
|
58
|
+
apt install libmbedtls-dev
|
|
59
|
+
|
|
60
|
+
# Arch
|
|
61
|
+
pacman -S mbedtls
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
</details>
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Request / Reply
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
require 'nng'
|
|
72
|
+
require 'async'
|
|
73
|
+
|
|
74
|
+
Sync do |task|
|
|
75
|
+
rep = NNG::Socket::Rep0.new
|
|
76
|
+
rep.listen('tcp://127.0.0.1:5555')
|
|
77
|
+
|
|
78
|
+
req = NNG::Socket::Req0.new
|
|
79
|
+
req.dial('tcp://127.0.0.1:5555')
|
|
80
|
+
|
|
81
|
+
task.async do
|
|
82
|
+
msg = rep.receive
|
|
83
|
+
rep.send("re: #{msg.body}")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
req.send('hello')
|
|
87
|
+
reply = req.receive
|
|
88
|
+
puts reply.body # => "re: hello"
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Pub / Sub
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
Sync do |task|
|
|
96
|
+
pub = NNG::Socket::Pub0.new
|
|
97
|
+
pub.listen('ipc:///tmp/pubsub.sock')
|
|
98
|
+
|
|
99
|
+
all = NNG::Socket::Sub0.new
|
|
100
|
+
all.dial('ipc:///tmp/pubsub.sock')
|
|
101
|
+
|
|
102
|
+
weather = NNG::Socket::Sub0.new(prefix: 'weather.')
|
|
103
|
+
weather.dial('ipc:///tmp/pubsub.sock')
|
|
104
|
+
|
|
105
|
+
sleep 0.01 # allow connections to establish
|
|
106
|
+
|
|
107
|
+
task.async do
|
|
108
|
+
pub.send('weather.rain')
|
|
109
|
+
pub.send('sports.goal')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
puts all.receive.body # => "weather.rain"
|
|
113
|
+
puts weather.receive.body # => "weather.rain"
|
|
114
|
+
puts all.receive.body # => "sports.goal"
|
|
115
|
+
# weather never receives "sports.goal"
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Push / Pull (Pipeline)
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
Sync do |task|
|
|
123
|
+
pull = NNG::Socket::Pull0.new
|
|
124
|
+
pull.listen('inproc://pipeline')
|
|
125
|
+
|
|
126
|
+
push = NNG::Socket::Push0.new
|
|
127
|
+
push.dial('inproc://pipeline')
|
|
128
|
+
|
|
129
|
+
task.async { push.send('work item') }
|
|
130
|
+
puts pull.receive.body # => "work item"
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Raw Mode Proxy
|
|
135
|
+
|
|
136
|
+
Raw mode sockets bypass the protocol state machine, enabling stateless message forwarding:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
Sync do |task|
|
|
140
|
+
backend = NNG::Socket::Rep0.new
|
|
141
|
+
backend.listen('inproc://backend')
|
|
142
|
+
|
|
143
|
+
proxy_fe = NNG::Socket::Rep0.new(raw: true)
|
|
144
|
+
proxy_fe.listen('inproc://frontend')
|
|
145
|
+
|
|
146
|
+
proxy_be = NNG::Socket::Req0.new(raw: true)
|
|
147
|
+
proxy_be.dial('inproc://backend')
|
|
148
|
+
|
|
149
|
+
client = NNG::Socket::Req0.new
|
|
150
|
+
client.dial('inproc://frontend')
|
|
151
|
+
|
|
152
|
+
task.async do
|
|
153
|
+
msg = backend.receive
|
|
154
|
+
backend.send("processed: #{msg.body}")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
task.async do
|
|
158
|
+
msg = proxy_fe.receive
|
|
159
|
+
proxy_be.forward(msg)
|
|
160
|
+
reply = proxy_be.receive
|
|
161
|
+
proxy_fe.forward(reply)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
client.send('job')
|
|
165
|
+
puts client.receive.body # => "processed: job"
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### TLS
|
|
170
|
+
|
|
171
|
+
Any socket type works over TLS — just use `tls+tcp://`. The [localhost](https://github.com/socketry/localhost) gem provides self-signed credentials for development:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
require 'localhost'
|
|
175
|
+
|
|
176
|
+
authority = Localhost::Authority.fetch
|
|
177
|
+
|
|
178
|
+
Sync do |task|
|
|
179
|
+
rep = NNG::Socket::Rep0.new
|
|
180
|
+
rep.listen('tls+tcp://127.0.0.1:5556',
|
|
181
|
+
cert: authority.certificate, key: authority.key)
|
|
182
|
+
|
|
183
|
+
req = NNG::Socket::Req0.new
|
|
184
|
+
req.dial('tls+tcp://127.0.0.1:5556',
|
|
185
|
+
ca: authority.issuer.certificate, server_name: 'localhost')
|
|
186
|
+
|
|
187
|
+
task.async do
|
|
188
|
+
msg = rep.receive
|
|
189
|
+
rep.send("secure: #{msg.body}")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
req.send('hello')
|
|
193
|
+
reply = req.receive
|
|
194
|
+
puts reply.body # => "secure: hello"
|
|
195
|
+
puts reply.pipe.tls_peer_cn # => "localhost"
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
<details>
|
|
200
|
+
<summary>TLS option reference</summary>
|
|
201
|
+
|
|
202
|
+
**`#listen`**
|
|
203
|
+
| Option | Description |
|
|
204
|
+
|--------|-------------|
|
|
205
|
+
| `cert:` | Server certificate (PEM string, `OpenSSL::X509::Certificate`, or `Pathname`) |
|
|
206
|
+
| `key:` | Private key (PEM string, `OpenSSL::PKey`, or `Pathname`) |
|
|
207
|
+
| `ca:` | CA certificate for client verification (mutual TLS) |
|
|
208
|
+
| `verify:` | Require client certificates (`false` by default) |
|
|
209
|
+
|
|
210
|
+
**`#dial`**
|
|
211
|
+
| Option | Description |
|
|
212
|
+
|--------|-------------|
|
|
213
|
+
| `ca:` | CA certificate to verify the server |
|
|
214
|
+
| `cert:` / `key:` | Client certificate (for mutual TLS) |
|
|
215
|
+
| `server_name:` | Expected server CN/SAN (defaults to host from URL) |
|
|
216
|
+
| `verify: false` | Skip server certificate verification |
|
|
217
|
+
|
|
218
|
+
</details>
|
|
219
|
+
|
|
220
|
+
### Pipe Introspection
|
|
221
|
+
|
|
222
|
+
Received messages carry a pipe reference for connection-level metadata:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
msg = rep.receive
|
|
226
|
+
pipe = msg.pipe
|
|
227
|
+
|
|
228
|
+
pipe.tls_verified? # => true
|
|
229
|
+
pipe.tls_peer_cn # => "localhost"
|
|
230
|
+
pipe.id # => 1
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Socket Options
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
sock = NNG::Socket::Req0.new
|
|
237
|
+
sock.name = 'my-socket'
|
|
238
|
+
sock.recv_timeout = 1.0 # seconds (default: nil = infinite)
|
|
239
|
+
sock.send_timeout = 1.0 # seconds (default: nil = infinite)
|
|
240
|
+
sock.recv_buffer = 128 # message count (default: protocol-specific)
|
|
241
|
+
sock.send_buffer = 128 # message count (default: protocol-specific)
|
|
242
|
+
sock.recv_max_size = 1_048_576 # bytes (default: 0 = no limit)
|
|
243
|
+
sock.reconnect_time = 0.1..30.0 # seconds min..max (default: 1.0..0.0)
|
|
244
|
+
|
|
245
|
+
sock.raw? # => false
|
|
246
|
+
sock.protocol_name # => "req"
|
|
247
|
+
sock.urls # => ["tcp://127.0.0.1:5555"]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Protocols
|
|
251
|
+
|
|
252
|
+
| Protocol | Class | Direction |
|
|
253
|
+
|----------|-------|-----------|
|
|
254
|
+
| Pair v0 | `Pair0` | bidirectional |
|
|
255
|
+
| Pair v1 | `Pair1` | bidirectional |
|
|
256
|
+
| Request | `Req0` | send + receive |
|
|
257
|
+
| Reply | `Rep0` | receive + send |
|
|
258
|
+
| Publish | `Pub0` | send only |
|
|
259
|
+
| Subscribe | `Sub0` | receive only |
|
|
260
|
+
| Push | `Push0` | send only |
|
|
261
|
+
| Pull | `Pull0` | receive only |
|
|
262
|
+
| Survey | `Surveyor0` | send + receive |
|
|
263
|
+
| Respond | `Respondent0` | receive + send |
|
|
264
|
+
| Bus | `Bus0` | bidirectional |
|
|
265
|
+
|
|
266
|
+
All classes live under `NNG::Socket::` and support `raw: true` for raw mode.
|
|
267
|
+
|
|
268
|
+
## Transports
|
|
269
|
+
|
|
270
|
+
| Transport | URL scheme | Notes |
|
|
271
|
+
|-----------|-----------|-------|
|
|
272
|
+
| In-process | `inproc://` | Fastest, same process only |
|
|
273
|
+
| IPC | `ipc://` | Unix domain sockets |
|
|
274
|
+
| Abstract | `abstract://` | Linux abstract namespace (no filesystem path) |
|
|
275
|
+
| TCP | `tcp://` | TCP/IP |
|
|
276
|
+
| TLS | `tls+tcp://` | TCP + TLS encryption (requires mbedTLS) |
|
|
277
|
+
|
|
278
|
+
## Performance
|
|
279
|
+
|
|
280
|
+
Benchmarked with benchmark-ips on Linux x86_64 (NNG 1.10.0, Ruby 4.0.1 +YJIT):
|
|
281
|
+
|
|
282
|
+
#### Throughput (push/pull)
|
|
283
|
+
|
|
284
|
+
| | inproc | abstract | ipc | tcp |
|
|
285
|
+
|---|--------|----------|-----|-----|
|
|
286
|
+
| **Async** | 47.3k/s | 14.3k/s | 13.3k/s | 8.4k/s |
|
|
287
|
+
| **Threads** | 41.7k/s | 15.4k/s | 16.6k/s | 10.1k/s |
|
|
288
|
+
|
|
289
|
+
#### Latency (req/rep roundtrip)
|
|
290
|
+
|
|
291
|
+
| | inproc | abstract | ipc | tcp |
|
|
292
|
+
|---|--------|----------|-----|-----|
|
|
293
|
+
| **Async** | 57 µs | 180 µs | 155 µs | 204 µs |
|
|
294
|
+
| **Threads** | 106 µs | 118 µs | 134 µs | 151 µs |
|
|
295
|
+
|
|
296
|
+
Async fibers deliver 1.9x lower inproc latency thanks to cheap context switching. See [`bench/`](bench/) for full results and scripts.
|
|
297
|
+
|
|
298
|
+
## Development
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
bundle install
|
|
302
|
+
bundle exec rake compile
|
|
303
|
+
bundle exec rake test
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
[MIT](LICENSE)
|
data/ext/rbnng/device.c
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#include "rbnng.h"
|
|
2
|
+
|
|
3
|
+
typedef struct {
|
|
4
|
+
nng_socket s1;
|
|
5
|
+
nng_socket s2;
|
|
6
|
+
int rv;
|
|
7
|
+
} device_args_t;
|
|
8
|
+
|
|
9
|
+
static void *
|
|
10
|
+
device_without_gvl(void *arg)
|
|
11
|
+
{
|
|
12
|
+
device_args_t *a = arg;
|
|
13
|
+
a->rv = nng_device(a->s1, a->s2);
|
|
14
|
+
return NULL;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static VALUE
|
|
18
|
+
device_start(VALUE self, VALUE sock1, VALUE sock2)
|
|
19
|
+
{
|
|
20
|
+
rbnng_socket_t *s1, *s2;
|
|
21
|
+
TypedData_Get_Struct(sock1, rbnng_socket_t, &rbnng_socket_type, s1);
|
|
22
|
+
TypedData_Get_Struct(sock2, rbnng_socket_t, &rbnng_socket_type, s2);
|
|
23
|
+
|
|
24
|
+
if (!s1->initialized || !s2->initialized)
|
|
25
|
+
rb_raise(rb_eRuntimeError, "socket not initialized");
|
|
26
|
+
|
|
27
|
+
device_args_t args;
|
|
28
|
+
args.s1 = s1->socket;
|
|
29
|
+
args.s2 = s2->socket;
|
|
30
|
+
|
|
31
|
+
rb_thread_call_without_gvl(device_without_gvl, &args, RUBY_UBF_IO, NULL);
|
|
32
|
+
|
|
33
|
+
if (args.rv != 0)
|
|
34
|
+
raise_nng_error(args.rv);
|
|
35
|
+
return Qnil;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
void
|
|
39
|
+
rbnng_device_init(VALUE nng_module)
|
|
40
|
+
{
|
|
41
|
+
VALUE mod = rb_define_module_under(nng_module, "Device");
|
|
42
|
+
rb_define_singleton_method(mod, "start", device_start, 2);
|
|
43
|
+
}
|