nio4r 2.0.0.pre-java → 2.1.0-java
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/.rspec +0 -1
- data/.rubocop.yml +31 -38
- data/.ruby-version +1 -0
- data/.travis.yml +9 -19
- data/CHANGES.md +94 -42
- data/Gemfile +11 -3
- data/Guardfile +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +43 -136
- data/Rakefile +2 -0
- data/examples/echo_server.rb +1 -0
- data/ext/libev/Changes +9 -13
- data/ext/libev/ev.c +100 -74
- data/ext/libev/ev.h +4 -9
- data/ext/libev/ev_epoll.c +6 -3
- data/ext/libev/ev_kqueue.c +8 -4
- data/ext/libev/ev_poll.c +6 -3
- data/ext/libev/ev_port.c +8 -4
- data/ext/libev/ev_select.c +4 -2
- data/ext/nio4r/bytebuffer.c +265 -257
- data/ext/nio4r/extconf.rb +3 -9
- data/ext/nio4r/monitor.c +93 -46
- data/ext/nio4r/nio4r.h +6 -16
- data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
- data/ext/nio4r/org/nio4r/Monitor.java +164 -0
- data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
- data/ext/nio4r/org/nio4r/Selector.java +278 -0
- data/ext/nio4r/selector.c +72 -64
- data/lib/nio.rb +3 -3
- data/lib/nio/bytebuffer.rb +179 -132
- data/lib/nio/monitor.rb +64 -4
- data/lib/nio/selector.rb +36 -13
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +25 -19
- data/spec/nio/acceptables_spec.rb +6 -4
- data/spec/nio/bytebuffer_spec.rb +323 -51
- data/spec/nio/monitor_spec.rb +122 -79
- data/spec/nio/selectables/pipe_spec.rb +5 -1
- data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
- data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
- data/spec/nio/selectables/udp_socket_spec.rb +2 -0
- data/spec/nio/selector_spec.rb +10 -4
- data/spec/spec_helper.rb +24 -3
- data/spec/support/selectable_examples.rb +7 -5
- data/tasks/extension.rake +2 -0
- data/tasks/rspec.rake +2 -0
- data/tasks/rubocop.rake +2 -0
- metadata +18 -15
- data/.rubocop_todo.yml +0 -35
data/spec/nio/monitor_spec.rb
CHANGED
@@ -1,113 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "socket"
|
3
5
|
|
4
6
|
RSpec.describe NIO::Monitor do
|
5
|
-
let(:
|
6
|
-
|
7
|
-
base_port = 12_345
|
8
|
-
tries = 10
|
9
|
-
|
10
|
-
server = tries.times do |n|
|
11
|
-
begin
|
12
|
-
break TCPServer.new(address, base_port + n)
|
13
|
-
rescue Errno::EADDRINUSE
|
14
|
-
retry
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
fail Errno::EADDRINUSE, "couldn't find an open port" unless server
|
19
|
-
client = TCPSocket.new(address, server.addr[1])
|
20
|
-
[server, client]
|
21
|
-
end
|
7
|
+
let(:addr) { "localhost" }
|
8
|
+
let(:port) { next_available_tcp_port }
|
22
9
|
|
23
|
-
let(:reader) {
|
24
|
-
let(:writer) {
|
10
|
+
let(:reader) { TCPServer.new(addr, port) }
|
11
|
+
let(:writer) { TCPSocket.new(addr, port) }
|
25
12
|
|
26
13
|
let(:selector) { NIO::Selector.new }
|
27
14
|
|
28
|
-
subject
|
29
|
-
|
30
|
-
after { selector.close }
|
15
|
+
subject(:monitor) { selector.register(writer, :rw) }
|
16
|
+
subject(:peer) { selector.register(reader, :r) }
|
31
17
|
|
32
|
-
before
|
33
|
-
|
34
|
-
after
|
18
|
+
before { reader }
|
19
|
+
before { writer }
|
20
|
+
after { reader.close }
|
21
|
+
after { writer.close }
|
22
|
+
after { selector.close }
|
35
23
|
|
36
|
-
|
37
|
-
|
38
|
-
|
24
|
+
describe "#interests" do
|
25
|
+
it "knows its interests" do
|
26
|
+
expect(peer.interests).to eq(:r)
|
27
|
+
expect(monitor.interests).to eq(:rw)
|
28
|
+
end
|
39
29
|
end
|
40
30
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
describe "#interests=" do
|
32
|
+
it "changes the interest set" do
|
33
|
+
expect(monitor.interests).not_to eq(:w)
|
34
|
+
monitor.interests = :w
|
35
|
+
expect(monitor.interests).to eq(:w)
|
36
|
+
end
|
46
37
|
|
47
|
-
|
48
|
-
|
38
|
+
it "raises EOFError if interests are changed after the monitor is closed" do
|
39
|
+
monitor.close
|
40
|
+
expect { monitor.interests = :rw }.to raise_error(EOFError)
|
41
|
+
end
|
49
42
|
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
|
44
|
+
describe "#add_interest" do
|
45
|
+
it "sets a new interest if it isn't currently registered" do
|
46
|
+
monitor.interests = :r
|
47
|
+
expect(monitor.interests).to eq(:r)
|
48
|
+
|
49
|
+
expect(monitor.add_interest(:w)).to eq(:rw)
|
50
|
+
expect(monitor.interests).to eq(:rw)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "acts idempotently" do
|
54
|
+
monitor.interests = :r
|
55
|
+
expect(monitor.interests).to eq(:r)
|
56
|
+
|
57
|
+
expect(monitor.add_interest(:r)).to eq(:r)
|
58
|
+
expect(monitor.interests).to eq(:r)
|
59
|
+
end
|
54
60
|
|
55
|
-
|
56
|
-
|
57
|
-
|
61
|
+
it "raises ArgumentError if given a bogus option" do
|
62
|
+
expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
|
63
|
+
end
|
58
64
|
end
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
describe "#remove_interest" do
|
67
|
+
it "removes an interest from the set" do
|
68
|
+
expect(monitor.interests).to eq(:rw)
|
69
|
+
|
70
|
+
expect(monitor.remove_interest(:r)).to eq(:w)
|
71
|
+
expect(monitor.interests).to eq(:w)
|
72
|
+
end
|
64
73
|
|
65
|
-
|
66
|
-
|
74
|
+
it "can clear the last interest" do
|
75
|
+
monitor.interests = :w
|
76
|
+
expect(monitor.interests).to eq(:w)
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
|
78
|
+
expect(monitor.remove_interest(:w)).to be_nil
|
79
|
+
expect(monitor.interests).to be_nil
|
80
|
+
end
|
71
81
|
|
72
|
-
|
82
|
+
it "acts idempotently" do
|
83
|
+
monitor.interests = :w
|
84
|
+
expect(monitor.interests).to eq(:w)
|
73
85
|
|
74
|
-
|
75
|
-
|
86
|
+
expect(monitor.remove_interest(:r)).to eq(:w)
|
87
|
+
expect(monitor.interests).to eq(:w)
|
88
|
+
end
|
76
89
|
|
77
|
-
|
78
|
-
|
79
|
-
|
90
|
+
it "raises ArgumentError if given a bogus option" do
|
91
|
+
expect { monitor.add_interest(:derp) }.to raise_error(ArgumentError)
|
92
|
+
end
|
80
93
|
end
|
81
94
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
monitor.interests = :w
|
87
|
-
expect(monitor.interests).to eq(:w)
|
95
|
+
describe "#io" do
|
96
|
+
it "knows its IO object" do
|
97
|
+
expect(monitor.io).to eq(writer)
|
98
|
+
end
|
88
99
|
end
|
89
100
|
|
90
|
-
|
91
|
-
|
92
|
-
|
101
|
+
describe "#selector" do
|
102
|
+
it "knows its selector" do
|
103
|
+
expect(monitor.selector).to eq(selector)
|
104
|
+
end
|
105
|
+
end
|
93
106
|
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
describe "#value=" do
|
108
|
+
it "stores arbitrary values" do
|
109
|
+
monitor.value = 42
|
110
|
+
expect(monitor.value).to eq(42)
|
111
|
+
end
|
97
112
|
end
|
98
113
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
114
|
+
describe "#readiness" do
|
115
|
+
it "knows what operations IO objects are ready for" do
|
116
|
+
# For whatever odd reason this breaks unless we eagerly evaluate monitor
|
117
|
+
reader_peer = peer
|
118
|
+
writer_peer = monitor
|
119
|
+
|
120
|
+
selected = selector.select(0)
|
121
|
+
expect(selected).to include(writer_peer)
|
122
|
+
|
123
|
+
expect(writer_peer.readiness).to eq(:w)
|
124
|
+
expect(writer_peer).not_to be_readable
|
125
|
+
expect(writer_peer).to be_writable
|
126
|
+
|
127
|
+
writer << "testing 1 2 3"
|
128
|
+
|
129
|
+
selected = selector.select(0)
|
130
|
+
expect(selected).to include(reader_peer)
|
131
|
+
|
132
|
+
expect(reader_peer.readiness).to eq(:r)
|
133
|
+
expect(reader_peer).to be_readable
|
134
|
+
expect(reader_peer).not_to be_writable
|
135
|
+
end
|
105
136
|
end
|
106
137
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
138
|
+
describe "#close" do
|
139
|
+
it "closes" do
|
140
|
+
expect(monitor).not_to be_closed
|
141
|
+
expect(selector.registered?(writer)).to be_truthy
|
142
|
+
|
143
|
+
monitor.close
|
144
|
+
expect(monitor).to be_closed
|
145
|
+
expect(selector.registered?(writer)).to be_falsey
|
146
|
+
end
|
147
|
+
|
148
|
+
it "closes even if the selector has been shutdown" do
|
149
|
+
expect(monitor).not_to be_closed
|
150
|
+
selector.close # forces shutdown
|
151
|
+
expect(monitor).not_to be_closed
|
152
|
+
monitor.close
|
153
|
+
expect(monitor).to be_closed
|
154
|
+
end
|
112
155
|
end
|
113
156
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
RSpec.describe "IO.pipe" do
|
@@ -6,6 +8,7 @@ RSpec.describe "IO.pipe" do
|
|
6
8
|
let :unreadable_subject do
|
7
9
|
pair.first
|
8
10
|
end
|
11
|
+
|
9
12
|
let :readable_subject do
|
10
13
|
pipe, peer = pair
|
11
14
|
peer << "data"
|
@@ -15,8 +18,9 @@ RSpec.describe "IO.pipe" do
|
|
15
18
|
let :writable_subject do
|
16
19
|
pair.last
|
17
20
|
end
|
21
|
+
|
18
22
|
let :unwritable_subject do
|
19
|
-
|
23
|
+
_reader, pipe = pair
|
20
24
|
|
21
25
|
# HACK: On OS X 10.8, this str must be larger than PIPE_BUF. Otherwise,
|
22
26
|
# the write is atomic and select() will return writable but write()
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "openssl"
|
3
5
|
|
4
|
-
RSpec.describe OpenSSL::SSL::SSLSocket
|
5
|
-
let(:
|
6
|
+
RSpec.describe OpenSSL::SSL::SSLSocket do
|
7
|
+
let(:addr) { "localhost" }
|
8
|
+
let(:port) { next_available_tcp_port }
|
6
9
|
|
7
10
|
let(:ssl_key) { OpenSSL::PKey::RSA.new(1024) }
|
8
11
|
|
@@ -29,8 +32,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket, if: RUBY_VERSION >= "1.9.0" do
|
|
29
32
|
end
|
30
33
|
|
31
34
|
let :readable_subject do
|
32
|
-
server = TCPServer.new(
|
33
|
-
client = TCPSocket.open(
|
35
|
+
server = TCPServer.new(addr, port)
|
36
|
+
client = TCPSocket.open(addr, port)
|
34
37
|
peer = server.accept
|
35
38
|
|
36
39
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -52,8 +55,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket, if: RUBY_VERSION >= "1.9.0" do
|
|
52
55
|
end
|
53
56
|
|
54
57
|
let :unreadable_subject do
|
55
|
-
server = TCPServer.new(
|
56
|
-
client = TCPSocket.new(
|
58
|
+
server = TCPServer.new(addr, port)
|
59
|
+
client = TCPSocket.new(addr, port)
|
57
60
|
peer = server.accept
|
58
61
|
|
59
62
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -72,8 +75,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket, if: RUBY_VERSION >= "1.9.0" do
|
|
72
75
|
end
|
73
76
|
|
74
77
|
let :writable_subject do
|
75
|
-
server = TCPServer.new(
|
76
|
-
client = TCPSocket.new(
|
78
|
+
server = TCPServer.new(addr, port)
|
79
|
+
client = TCPSocket.new(addr, port)
|
77
80
|
peer = server.accept
|
78
81
|
|
79
82
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -92,8 +95,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket, if: RUBY_VERSION >= "1.9.0" do
|
|
92
95
|
end
|
93
96
|
|
94
97
|
let :unwritable_subject do
|
95
|
-
server = TCPServer.new(
|
96
|
-
client = TCPSocket.
|
98
|
+
server = TCPServer.new(addr, port)
|
99
|
+
client = TCPSocket.new(addr, port)
|
97
100
|
peer = server.accept
|
98
101
|
|
99
102
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -140,8 +143,8 @@ RSpec.describe OpenSSL::SSL::SSLSocket, if: RUBY_VERSION >= "1.9.0" do
|
|
140
143
|
let :pair do
|
141
144
|
pending "figure out why newly created sockets are selecting readable immediately"
|
142
145
|
|
143
|
-
server = TCPServer.new(
|
144
|
-
client = TCPSocket.
|
146
|
+
server = TCPServer.new(addr, port)
|
147
|
+
client = TCPSocket.new(addr, port)
|
145
148
|
peer = server.accept
|
146
149
|
|
147
150
|
ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
|
@@ -1,20 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
RSpec.describe TCPSocket do
|
4
|
-
|
5
|
-
let(:
|
6
|
+
let(:addr) { "127.0.0.1" }
|
7
|
+
let(:port) { next_available_tcp_port }
|
6
8
|
|
7
9
|
let :readable_subject do
|
8
|
-
server = TCPServer.new(
|
9
|
-
sock = TCPSocket.
|
10
|
+
server = TCPServer.new(addr, port)
|
11
|
+
sock = TCPSocket.new(addr, port)
|
10
12
|
peer = server.accept
|
11
13
|
peer << "data"
|
12
14
|
sock
|
13
15
|
end
|
14
16
|
|
15
17
|
let :unreadable_subject do
|
16
|
-
TCPServer.new(
|
17
|
-
sock = TCPSocket.new(
|
18
|
+
TCPServer.new(addr, port)
|
19
|
+
sock = TCPSocket.new(addr, port)
|
18
20
|
|
19
21
|
# Sanity check to make sure we actually produced an unreadable socket
|
20
22
|
if select([sock], [], [], 0)
|
@@ -25,21 +27,25 @@ RSpec.describe TCPSocket do
|
|
25
27
|
end
|
26
28
|
|
27
29
|
let :writable_subject do
|
28
|
-
TCPServer.new(
|
29
|
-
TCPSocket.new(
|
30
|
+
TCPServer.new(addr, port)
|
31
|
+
TCPSocket.new(addr, port)
|
30
32
|
end
|
31
33
|
|
32
34
|
let :unwritable_subject do
|
33
|
-
server = TCPServer.new(
|
34
|
-
sock = TCPSocket.
|
35
|
-
peer = server.accept
|
35
|
+
server = TCPServer.new(addr, port)
|
36
|
+
sock = TCPSocket.new(addr, port)
|
36
37
|
|
37
|
-
|
38
|
+
# TODO: close this socket
|
39
|
+
_peer = server.accept
|
40
|
+
|
41
|
+
loop do
|
38
42
|
sock.write_nonblock "X" * 1024
|
39
|
-
_, writers = select
|
40
|
-
end while writers && writers.include?(sock)
|
43
|
+
_, writers = Kernel.select([], [sock], [], 0)
|
41
44
|
|
42
|
-
|
45
|
+
break unless writers && writers.include?(sock)
|
46
|
+
end
|
47
|
+
|
48
|
+
# HAX: I think the kernel might manage to drain its buffer a bit even after
|
43
49
|
# the socket first goes unwritable. Attempt to sleep past this and then
|
44
50
|
# attempt to write again
|
45
51
|
sleep 0.1
|
@@ -59,8 +65,8 @@ RSpec.describe TCPSocket do
|
|
59
65
|
end
|
60
66
|
|
61
67
|
let :pair do
|
62
|
-
server = TCPServer.new(
|
63
|
-
client = TCPSocket.
|
68
|
+
server = TCPServer.new(addr, port)
|
69
|
+
client = TCPSocket.new(addr, port)
|
64
70
|
[client, server.accept]
|
65
71
|
end
|
66
72
|
|
@@ -69,20 +75,25 @@ RSpec.describe TCPSocket do
|
|
69
75
|
it_behaves_like "an NIO bidirectional stream"
|
70
76
|
|
71
77
|
context :connect do
|
72
|
-
it "selects writable when connected" do
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
78
|
+
it "selects writable when connected", retry: 5 do # retry: Flaky on OS X
|
79
|
+
begin
|
80
|
+
server = TCPServer.new(addr, port)
|
81
|
+
selector = NIO::Selector.new
|
82
|
+
|
83
|
+
client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
84
|
+
monitor = selector.register(client, :w)
|
85
|
+
|
86
|
+
expect do
|
87
|
+
client.connect_nonblock Socket.sockaddr_in(port, addr)
|
88
|
+
end.to raise_exception Errno::EINPROGRESS
|
89
|
+
|
90
|
+
expect(selector.select(0.001)).to include monitor
|
91
|
+
result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
|
92
|
+
expect(result.unpack("i").first).to be_zero
|
93
|
+
ensure
|
94
|
+
server.close rescue nil
|
95
|
+
selector.close rescue nil
|
96
|
+
end
|
86
97
|
end
|
87
98
|
end
|
88
99
|
end
|
data/spec/nio/selector_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "timeout"
|
3
5
|
|
@@ -11,6 +13,12 @@ RSpec.describe NIO::Selector do
|
|
11
13
|
let(:reader) { pair.first }
|
12
14
|
let(:writer) { pair.last }
|
13
15
|
|
16
|
+
context "backend" do
|
17
|
+
it "knows its backend" do
|
18
|
+
expect(subject.backend).to be_a Symbol
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
context "register" do
|
15
23
|
it "registers IO objects" do
|
16
24
|
monitor = subject.register(reader, :r)
|
@@ -43,9 +51,7 @@ RSpec.describe NIO::Selector do
|
|
43
51
|
|
44
52
|
it "reports if it is empty" do
|
45
53
|
expect(subject).to be_empty
|
46
|
-
|
47
|
-
monitor = subject.register(reader, :r)
|
48
|
-
|
54
|
+
subject.register(reader, :r)
|
49
55
|
expect(subject).not_to be_empty
|
50
56
|
end
|
51
57
|
|
@@ -96,7 +102,7 @@ RSpec.describe NIO::Selector do
|
|
96
102
|
|
97
103
|
thread = Thread.new do
|
98
104
|
started_at = Time.now
|
99
|
-
expect(subject.select).to
|
105
|
+
expect(subject.select).to eq []
|
100
106
|
Time.now - started_at
|
101
107
|
end
|
102
108
|
|