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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +31 -38
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +9 -19
  6. data/CHANGES.md +94 -42
  7. data/Gemfile +11 -3
  8. data/Guardfile +10 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +43 -136
  11. data/Rakefile +2 -0
  12. data/examples/echo_server.rb +1 -0
  13. data/ext/libev/Changes +9 -13
  14. data/ext/libev/ev.c +100 -74
  15. data/ext/libev/ev.h +4 -9
  16. data/ext/libev/ev_epoll.c +6 -3
  17. data/ext/libev/ev_kqueue.c +8 -4
  18. data/ext/libev/ev_poll.c +6 -3
  19. data/ext/libev/ev_port.c +8 -4
  20. data/ext/libev/ev_select.c +4 -2
  21. data/ext/nio4r/bytebuffer.c +265 -257
  22. data/ext/nio4r/extconf.rb +3 -9
  23. data/ext/nio4r/monitor.c +93 -46
  24. data/ext/nio4r/nio4r.h +6 -16
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +193 -209
  26. data/ext/nio4r/org/nio4r/Monitor.java +164 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +13 -391
  28. data/ext/nio4r/org/nio4r/Selector.java +278 -0
  29. data/ext/nio4r/selector.c +72 -64
  30. data/lib/nio.rb +3 -3
  31. data/lib/nio/bytebuffer.rb +179 -132
  32. data/lib/nio/monitor.rb +64 -4
  33. data/lib/nio/selector.rb +36 -13
  34. data/lib/nio/version.rb +1 -1
  35. data/nio4r.gemspec +25 -19
  36. data/spec/nio/acceptables_spec.rb +6 -4
  37. data/spec/nio/bytebuffer_spec.rb +323 -51
  38. data/spec/nio/monitor_spec.rb +122 -79
  39. data/spec/nio/selectables/pipe_spec.rb +5 -1
  40. data/spec/nio/selectables/ssl_socket_spec.rb +15 -12
  41. data/spec/nio/selectables/tcp_socket_spec.rb +42 -31
  42. data/spec/nio/selectables/udp_socket_spec.rb +2 -0
  43. data/spec/nio/selector_spec.rb +10 -4
  44. data/spec/spec_helper.rb +24 -3
  45. data/spec/support/selectable_examples.rb +7 -5
  46. data/tasks/extension.rake +2 -0
  47. data/tasks/rspec.rake +2 -0
  48. data/tasks/rubocop.rake +2 -0
  49. metadata +18 -15
  50. data/.rubocop_todo.yml +0 -35
@@ -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(:example_peers) do
6
- address = "127.0.0.1"
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) { example_peers.first }
24
- let(:writer) { example_peers.last }
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 { selector.register(reader, :r) }
29
- let(:peer) { selector.register(writer, :rw) }
30
- after { selector.close }
15
+ subject(:monitor) { selector.register(writer, :rw) }
16
+ subject(:peer) { selector.register(reader, :r) }
31
17
 
32
- before { example_peers } # open server and client automatically
33
- after { reader.close }
34
- after { writer.close }
18
+ before { reader }
19
+ before { writer }
20
+ after { reader.close }
21
+ after { writer.close }
22
+ after { selector.close }
35
23
 
36
- it "knows its interests" do
37
- expect(subject.interests).to eq(:r)
38
- expect(peer.interests).to eq(:rw)
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
- it "changes the interest set" do
42
- expect(peer.interests).not_to eq(:w)
43
- peer.interests = :w
44
- expect(peer.interests).to eq(:w)
45
- end
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
- it "knows its IO object" do
48
- expect(subject.io).to eq(reader)
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
- it "knows its selector" do
52
- expect(subject.selector).to eq(selector)
53
- end
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
- it "stores arbitrary values" do
56
- subject.value = 42
57
- expect(subject.value).to eq(42)
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
- it "knows what operations IO objects are ready for" do
61
- # For whatever odd reason this breaks unless we eagerly evaluate subject
62
- reader_monitor = subject
63
- writer_monitor = peer
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
- selected = selector.select(0)
66
- expect(selected).to include(writer_monitor)
74
+ it "can clear the last interest" do
75
+ monitor.interests = :w
76
+ expect(monitor.interests).to eq(:w)
67
77
 
68
- expect(writer_monitor.readiness).to eq(:w)
69
- expect(writer_monitor).not_to be_readable
70
- expect(writer_monitor).to be_writable
78
+ expect(monitor.remove_interest(:w)).to be_nil
79
+ expect(monitor.interests).to be_nil
80
+ end
71
81
 
72
- writer << "testing 1 2 3"
82
+ it "acts idempotently" do
83
+ monitor.interests = :w
84
+ expect(monitor.interests).to eq(:w)
73
85
 
74
- selected = selector.select(0)
75
- expect(selected).to include(reader_monitor)
86
+ expect(monitor.remove_interest(:r)).to eq(:w)
87
+ expect(monitor.interests).to eq(:w)
88
+ end
76
89
 
77
- expect(reader_monitor.readiness).to eq(:r)
78
- expect(reader_monitor).to be_readable
79
- expect(reader_monitor).not_to be_writable
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
- it "changes current interests with #interests=" do
83
- client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
84
- monitor = selector.register(client, :r)
85
- expect(monitor.interests).to eq(:r)
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
- it "closes" do
91
- expect(subject).not_to be_closed
92
- expect(selector.registered?(reader)).to be_truthy
101
+ describe "#selector" do
102
+ it "knows its selector" do
103
+ expect(monitor.selector).to eq(selector)
104
+ end
105
+ end
93
106
 
94
- subject.close
95
- expect(subject).to be_closed
96
- expect(selector.registered?(reader)).to be_falsey
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
- it "closes even if the selector has been shutdown" do
100
- expect(subject).not_to be_closed
101
- selector.close # forces shutdown
102
- expect(subject).not_to be_closed
103
- subject.close
104
- expect(subject).to be_closed
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
- it "changes the interest set after monitor closed" do
108
- # check for changing the interests on the go after closed expected to fail
109
- expect(subject.interests).not_to eq(:rw)
110
- subject.close # forced shutdown
111
- expect { subject.interests = :rw }.to raise_error(TypeError)
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
- reader, pipe = IO.pipe
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, if: RUBY_VERSION >= "1.9.0" do
5
- let(:tcp_port) { 34_567 }
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("localhost", tcp_port)
33
- client = TCPSocket.open("localhost", tcp_port)
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("localhost", tcp_port + 1)
56
- client = TCPSocket.new("localhost", tcp_port + 1)
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("localhost", tcp_port + 2)
76
- client = TCPSocket.new("localhost", tcp_port + 2)
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("localhost", tcp_port + 3)
96
- client = TCPSocket.open("localhost", tcp_port + 3)
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("localhost", tcp_port + 4)
144
- client = TCPSocket.open("localhost", tcp_port + 4)
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
- port_offset = 0
5
- let(:tcp_port) { 12_345 + (port_offset += 1) }
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("localhost", tcp_port)
9
- sock = TCPSocket.open("localhost", tcp_port)
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("localhost", tcp_port)
17
- sock = TCPSocket.new("localhost", tcp_port)
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("localhost", tcp_port)
29
- TCPSocket.new("localhost", tcp_port)
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("localhost", tcp_port)
34
- sock = TCPSocket.open("localhost", tcp_port)
35
- peer = server.accept
35
+ server = TCPServer.new(addr, port)
36
+ sock = TCPSocket.new(addr, port)
36
37
 
37
- begin
38
+ # TODO: close this socket
39
+ _peer = server.accept
40
+
41
+ loop do
38
42
  sock.write_nonblock "X" * 1024
39
- _, writers = select [], [sock], [], 0
40
- end while writers && writers.include?(sock)
43
+ _, writers = Kernel.select([], [sock], [], 0)
41
44
 
42
- # I think the kernel might manage to drain its buffer a bit even after
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("localhost", tcp_port)
63
- client = TCPSocket.open("localhost", tcp_port)
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
- selector = NIO::Selector.new
74
- server = TCPServer.new("127.0.0.1", tcp_port)
75
-
76
- client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
77
- monitor = selector.register(client, :w)
78
-
79
- expect do
80
- client.connect_nonblock Socket.sockaddr_in(tcp_port, "127.0.0.1")
81
- end.to raise_exception Errno::EINPROGRESS
82
-
83
- expect(selector.select(0.001)).to include monitor
84
- result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
85
- expect(result.unpack("i").first).to be_zero
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  RSpec.describe UDPSocket do
@@ -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 be_nil
105
+ expect(subject.select).to eq []
100
106
  Time.now - started_at
101
107
  end
102
108