nio4r 2.5.8 → 2.7.4

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 (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/{CHANGES.md → changes.md} +48 -0
  4. data/ext/nio4r/bytebuffer.c +75 -38
  5. data/ext/nio4r/extconf.rb +19 -1
  6. data/ext/nio4r/monitor.c +47 -22
  7. data/ext/nio4r/nio4r.h +1 -5
  8. data/ext/nio4r/nio4r_ext.c +4 -0
  9. data/ext/nio4r/org/nio4r/ByteBuffer.java +1 -1
  10. data/ext/nio4r/org/nio4r/Monitor.java +2 -2
  11. data/ext/nio4r/org/nio4r/Selector.java +2 -2
  12. data/ext/nio4r/selector.c +72 -48
  13. data/lib/nio/bytebuffer.rb +6 -0
  14. data/lib/nio/monitor.rb +7 -0
  15. data/lib/nio/selector.rb +15 -0
  16. data/lib/nio/version.rb +6 -1
  17. data/lib/nio.rb +9 -0
  18. data/lib/nio4r.rb +7 -0
  19. data/license.md +80 -0
  20. data/readme.md +91 -0
  21. data.tar.gz.sig +3 -0
  22. metadata +96 -75
  23. metadata.gz.sig +0 -0
  24. data/.github/workflows/workflow.yml +0 -47
  25. data/.gitignore +0 -21
  26. data/.rspec +0 -4
  27. data/.rubocop.yml +0 -100
  28. data/Gemfile +0 -18
  29. data/README.md +0 -133
  30. data/Rakefile +0 -8
  31. data/examples/echo_server.rb +0 -47
  32. data/logo.png +0 -0
  33. data/nio4r.gemspec +0 -42
  34. data/rakelib/extension.rake +0 -15
  35. data/rakelib/rspec.rake +0 -9
  36. data/rakelib/rubocop.rake +0 -5
  37. data/spec/nio/acceptables_spec.rb +0 -32
  38. data/spec/nio/bytebuffer_spec.rb +0 -354
  39. data/spec/nio/monitor_spec.rb +0 -162
  40. data/spec/nio/selectables/pipe_spec.rb +0 -47
  41. data/spec/nio/selectables/ssl_socket_spec.rb +0 -194
  42. data/spec/nio/selectables/tcp_socket_spec.rb +0 -101
  43. data/spec/nio/selectables/udp_socket_spec.rb +0 -48
  44. data/spec/nio/selector_spec.rb +0 -240
  45. data/spec/spec_helper.rb +0 -20
  46. data/spec/support/selectable_examples.rb +0 -85
@@ -1,194 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe OpenSSL::SSL::SSLSocket do
6
-
7
- require "openssl"
8
-
9
- before(:all) do
10
- @tls = []
11
- end
12
-
13
- let(:addr) { "127.0.0.1" }
14
-
15
- let(:ssl_key) { OpenSSL::PKey::RSA.new(2048) }
16
-
17
- let(:ssl_cert) do
18
- name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]])
19
- OpenSSL::X509::Certificate.new.tap do |cert|
20
- cert.version = 2
21
- cert.serial = 1
22
- cert.issuer = name
23
- cert.subject = name
24
- cert.not_before = Time.now
25
- cert.not_after = Time.now + (7 * 24 * 60 * 60)
26
- cert.public_key = ssl_key.public_key
27
-
28
- cert.sign(ssl_key, OpenSSL::Digest::SHA256.new)
29
- end
30
- end
31
-
32
- let(:ssl_server_context) do
33
- OpenSSL::SSL::SSLContext.new.tap do |ctx|
34
- ctx.cert = ssl_cert
35
- ctx.key = ssl_key
36
- unless @tls.empty?
37
- if ctx.respond_to? :set_minmax_proto_version, true
38
- ctx.max_version = @tls[0]
39
- else
40
- ctx.ssl_version = @tls[1]
41
- end
42
- end
43
- end
44
- end
45
-
46
- let :readable_subject do
47
- server = TCPServer.new(addr, 0)
48
- client = TCPSocket.open(addr, server.local_address.ip_port)
49
- peer = server.accept
50
-
51
- ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
52
- ssl_peer.sync_close = true
53
-
54
- ssl_client = OpenSSL::SSL::SSLSocket.new(client)
55
- ssl_client.sync_close = true
56
-
57
- # SSLSocket#connect and #accept are blocking calls.
58
- thread = Thread.new { ssl_client.connect }
59
-
60
- ssl_peer.accept
61
- ssl_peer << "data"
62
- ssl_peer.flush
63
-
64
- thread.join
65
-
66
- pending "Failed to produce a readable socket" unless select([ssl_client], [], [], 10)
67
- ssl_client
68
- end
69
-
70
- let :unreadable_subject do
71
- server = TCPServer.new(addr, 0)
72
- client = TCPSocket.new(addr, server.local_address.ip_port)
73
- peer = server.accept
74
-
75
- ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
76
- ssl_peer.sync_close = true
77
-
78
- ssl_client = OpenSSL::SSL::SSLSocket.new(client)
79
- ssl_client.sync_close = true
80
-
81
- # SSLSocket#connect and #accept are blocking calls.
82
- thread = Thread.new { ssl_client.connect }
83
- ssl_peer.accept
84
- thread.join
85
-
86
- if ssl_client.ssl_version == "TLSv1.3"
87
- expect(ssl_client.read_nonblock(1, exception: false)).to eq(:wait_readable)
88
- end
89
-
90
- pending "Failed to produce an unreadable socket" if select([ssl_client], [], [], 0)
91
- ssl_client
92
- end
93
-
94
- let :writable_subject do
95
- server = TCPServer.new(addr, 0)
96
- client = TCPSocket.new(addr, server.local_address.ip_port)
97
- peer = server.accept
98
-
99
- ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
100
- ssl_peer.sync_close = true
101
-
102
- ssl_client = OpenSSL::SSL::SSLSocket.new(client)
103
- ssl_client.sync_close = true
104
-
105
- # SSLSocket#connect and #accept are blocking calls.
106
- thread = Thread.new { ssl_client.connect }
107
-
108
- ssl_peer.accept
109
- thread.join
110
-
111
- ssl_client
112
- end
113
-
114
- let :unwritable_subject do
115
- server = TCPServer.new(addr, 0)
116
- client = TCPSocket.new(addr, server.local_address.ip_port)
117
- peer = server.accept
118
-
119
- ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
120
- ssl_peer.sync_close = true
121
-
122
- ssl_client = OpenSSL::SSL::SSLSocket.new(client)
123
- ssl_client.sync_close = true
124
-
125
- # SSLSocket#connect and #accept are blocking calls.
126
- thread = Thread.new { ssl_client.connect }
127
-
128
- ssl_peer.accept
129
- thread.join
130
-
131
- cntr = 0
132
- begin
133
- count = ssl_client.write_nonblock "X" * 1024
134
- expect(count).not_to eq(0)
135
- cntr += 1
136
- t = select [], [ssl_client], [], 0
137
- rescue IO::WaitReadable, IO::WaitWritable
138
- pending "SSL will report writable but not accept writes"
139
- end while t && t[1].include?(ssl_client) && cntr < 30
140
-
141
- # I think the kernel might manage to drain its buffer a bit even after
142
- # the socket first goes unwritable. Attempt to sleep past this and then
143
- # attempt to write again
144
- sleep 0.1
145
-
146
- # Once more for good measure!
147
- begin
148
- # ssl_client.write_nonblock "X" * 1024
149
- loop { ssl_client.write_nonblock "X" * 1024 }
150
- rescue OpenSSL::SSL::SSLError
151
- end
152
-
153
- # Sanity check to make sure we actually produced an unwritable socket
154
- if select([], [ssl_client], [], 0)
155
- pending "Failed to produce an unwritable socket"
156
- end
157
-
158
- ssl_client
159
- end
160
-
161
- let :pair do
162
- server = TCPServer.new(addr, 0)
163
- client = TCPSocket.new(addr, server.local_address.ip_port)
164
- peer = server.accept
165
-
166
- ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context)
167
- ssl_peer.sync_close = true
168
-
169
- ssl_client = OpenSSL::SSL::SSLSocket.new(client)
170
- ssl_client.sync_close = true
171
-
172
- # SSLSocket#connect and #accept are blocking calls.
173
- thread = Thread.new { ssl_client.connect }
174
- ssl_peer.accept
175
-
176
- [thread.value, ssl_peer]
177
- end
178
-
179
- describe "using TLS 1.2" do
180
- before(:all) do
181
- @tls = %i[TLS1_2 TLSv1_2]
182
- end
183
- it_behaves_like "an NIO selectable"
184
- it_behaves_like "an NIO selectable stream"
185
- end
186
-
187
- describe "using TLS 1.3", if: OpenSSL::SSL.const_defined?(:TLS1_3_VERSION) do
188
- before(:all) do
189
- @tls = %i[TLS1_3 TLSv1_3]
190
- end
191
- it_behaves_like "an NIO selectable"
192
- it_behaves_like "an NIO selectable stream", true
193
- end
194
- end
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe TCPSocket do
6
- let(:addr) { "127.0.0.1" }
7
-
8
- let :readable_subject do
9
- server = TCPServer.new(addr, 0)
10
- sock = TCPSocket.new(addr, server.local_address.ip_port)
11
- peer = server.accept
12
-
13
- peer << "Xdata"
14
- peer.flush
15
- sock.read(1)
16
-
17
- sock
18
- end
19
-
20
- let :unreadable_subject do
21
- server = TCPServer.new(addr, 0)
22
- sock = TCPSocket.new(addr, server.local_address.ip_port)
23
-
24
- # Sanity check to make sure we actually produced an unreadable socket
25
- pending "Failed to produce an unreadable socket" if select([sock], [], [], 0)
26
-
27
- sock
28
- end
29
-
30
- let :writable_subject do
31
- server = TCPServer.new(addr, 0)
32
- TCPSocket.new(addr, server.local_address.ip_port)
33
- end
34
-
35
- let :unwritable_subject do
36
- server = TCPServer.new(addr, 0)
37
- sock = TCPSocket.new(addr, server.local_address.ip_port)
38
-
39
- # TODO: close this socket
40
- _peer = server.accept
41
-
42
- loop do
43
- sock.write_nonblock "X" * 1024
44
- _, writers = Kernel.select([], [sock], [], 0)
45
-
46
- break unless writers && writers.include?(sock)
47
- end
48
-
49
- # HAX: I think the kernel might manage to drain its buffer a bit even after
50
- # the socket first goes unwritable. Attempt to sleep past this and then
51
- # attempt to write again
52
- sleep 0.1
53
-
54
- # Once more for good measure!
55
- begin
56
- sock.write_nonblock "X" * 1024
57
- rescue Errno::EWOULDBLOCK
58
- end
59
-
60
- # Sanity check to make sure we actually produced an unwritable socket
61
- pending "Failed to produce an unwritable socket" if select([], [sock], [], 0)
62
-
63
- sock
64
- end
65
-
66
- let :pair do
67
- server = TCPServer.new(addr, 0)
68
- client = TCPSocket.new(addr, server.local_address.ip_port)
69
- [client, server.accept]
70
- end
71
-
72
- it_behaves_like "an NIO selectable"
73
- it_behaves_like "an NIO selectable stream"
74
- it_behaves_like "an NIO bidirectional stream"
75
-
76
- context :connect do
77
- include_context NIO::Selector
78
-
79
- it "selects writable when connected" do
80
- begin
81
- server = TCPServer.new(addr, 0)
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 server.local_address
88
- end.to raise_exception Errno::EINPROGRESS
89
-
90
- ready = selector.select(1)
91
-
92
- expect(ready).to include monitor
93
- result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
94
- expect(result.unpack("i").first).to be_zero
95
- ensure
96
- server.close rescue nil
97
- selector.close rescue nil
98
- end
99
- end
100
- end
101
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe UDPSocket, if: !defined?(JRUBY_VERSION) do
6
- let(:udp_port) { 23_456 }
7
-
8
- let :readable_subject do
9
- server = UDPSocket.new
10
- server.bind("127.0.0.1", 0)
11
-
12
- peer = UDPSocket.new
13
- peer.send("hi there", 0, "127.0.0.1", server.local_address.ip_port)
14
-
15
- server
16
- end
17
-
18
- let :unreadable_subject do
19
- sock = UDPSocket.new
20
- sock.bind("127.0.0.1", 0)
21
- sock
22
- end
23
-
24
- let :writable_subject do
25
- server = UDPSocket.new
26
- server.bind("127.0.0.1", 0)
27
-
28
- peer = UDPSocket.new
29
- peer.connect("127.0.0.1", server.local_address.ip_port)
30
-
31
- cntr = 0
32
- begin
33
- peer.send("X" * 1024, 0)
34
- cntr += 1
35
- t = select [], [peer], [], 0
36
- rescue Errno::ECONNREFUSED => e
37
- skip "Couldn't make writable UDPSocket subject: #{e.class}: #{e}"
38
- end while t && t[1].include?(peer) && cntr < 5
39
-
40
- peer
41
- end
42
-
43
- let :unwritable_subject do
44
- pending "come up with a UDPSocket that's blocked on writing"
45
- end
46
-
47
- it_behaves_like "an NIO selectable"
48
- end
@@ -1,240 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "timeout"
5
-
6
- RSpec.describe NIO::Selector do
7
- let(:pair) { IO.pipe }
8
- let(:reader) { pair.first }
9
- let(:writer) { pair.last }
10
-
11
- context ".backends" do
12
- it "knows all supported backends" do
13
- expect(described_class.backends).to be_a Array
14
- expect(described_class.backends.first).to be_a Symbol
15
- end
16
- end
17
-
18
- context "#initialize" do
19
- it "allows explicitly specifying a backend" do |example|
20
- backend = described_class.backends.first
21
- selector = described_class.new(backend)
22
- expect(selector.backend).to eq backend
23
-
24
- example.reporter.message "Supported backends: #{described_class.backends}"
25
- end
26
-
27
- it "automatically selects a backend if none or nil is specified" do
28
- expect(described_class.new.backend).to eq described_class.new(nil).backend
29
- end
30
-
31
- it "raises ArgumentError if given an invalid backend" do
32
- expect { described_class.new(:derp) }.to raise_error ArgumentError
33
- end
34
-
35
- it "raises TypeError if given a non-Symbol parameter" do
36
- expect { described_class.new(42).to raise_error TypeError }
37
- end
38
- end
39
-
40
- context "backend" do
41
- it "knows its backend" do |example|
42
- expect(subject.backend).to be_a Symbol
43
-
44
- example.reporter.message "Current backend: #{subject.backend}"
45
- end
46
- end
47
-
48
- context "register" do
49
- it "registers IO objects" do
50
- monitor = subject.register(reader, :r)
51
- expect(monitor).not_to be_closed
52
- end
53
-
54
- it "raises TypeError if asked to register non-IO objects" do
55
- expect { subject.register(42, :r) }.to raise_exception TypeError
56
- end
57
-
58
- it "raises when asked to register after closing" do
59
- subject.close
60
- expect { subject.register(reader, :r) }.to raise_exception IOError
61
- end
62
- end
63
-
64
- it "knows which IO objects are registered" do
65
- subject.register(reader, :r)
66
- expect(subject).to be_registered(reader)
67
- expect(subject).not_to be_registered(writer)
68
- end
69
-
70
- it "deregisters IO objects" do
71
- subject.register(reader, :r)
72
-
73
- monitor = subject.deregister(reader)
74
- expect(subject).not_to be_registered(reader)
75
- expect(monitor).to be_closed
76
- end
77
-
78
- it "allows deregistering closed IO objects" do
79
- subject.register(reader, :r)
80
- reader.close
81
-
82
- expect do
83
- subject.deregister(reader)
84
- end.not_to raise_error
85
- end
86
-
87
- it "reports if it is empty" do
88
- expect(subject).to be_empty
89
- subject.register(reader, :r)
90
- expect(subject).not_to be_empty
91
- end
92
-
93
- # This spec might seem a bit silly, but this actually something the
94
- # Java NIO API specifically precludes that we need to work around
95
- it "allows reregistration of the same IO object across select calls" do
96
- monitor = subject.register(reader, :r)
97
- writer << "ohai"
98
-
99
- expect(subject.select).to include monitor
100
- expect(reader.read(4)).to eq("ohai")
101
- subject.deregister(reader)
102
-
103
- new_monitor = subject.register(reader, :r)
104
- writer << "thar"
105
- expect(subject.select).to include new_monitor
106
- expect(reader.read(4)).to eq("thar")
107
- end
108
-
109
- context "timeouts" do
110
- let(:select_precision) {0.2}
111
- let(:timeout) {2.0}
112
- let(:payload) {"hi there"}
113
-
114
- it "waits for timeout when selecting from empty selector" do
115
- started_at = Time.now
116
- expect(subject.select(timeout)).to be_nil
117
- expect(Time.now - started_at).to be_within(select_precision).of(timeout)
118
- end
119
-
120
- it "waits for a timeout when selecting with reader" do
121
- monitor = subject.register(reader, :r)
122
-
123
- writer << payload
124
-
125
- started_at = Time.now
126
- expect(subject.select(timeout)).to include monitor
127
- expect(Time.now - started_at).to be_within(select_precision).of(0)
128
- reader.read_nonblock(payload.size)
129
-
130
- started_at = Time.now
131
- expect(subject.select(timeout)).to be_nil
132
- expect(Time.now - started_at).to be_within(select_precision).of(timeout)
133
- end
134
-
135
- it "raises ArgumentError if given a negative timeout" do
136
- subject.register(reader, :r)
137
-
138
- expect do
139
- subject.select(-1)
140
- end.to raise_exception(ArgumentError)
141
- end
142
- end
143
-
144
- context "wakeup" do
145
- let(:select_precision) {0.2}
146
-
147
- it "wakes up if signaled to from another thread" do
148
- subject.register(reader, :r)
149
-
150
- thread = Thread.new do
151
- started_at = Time.now
152
- expect(subject.select).to eq []
153
- Time.now - started_at
154
- end
155
-
156
- timeout = 0.1
157
- sleep timeout
158
- subject.wakeup
159
-
160
- expect(thread.value).to be_within(select_precision).of(timeout)
161
- end
162
-
163
- it "raises IOError if asked to wake up a closed selector" do
164
- subject.close
165
- expect(subject).to be_closed
166
-
167
- expect { subject.wakeup }.to raise_exception IOError
168
- end
169
- end
170
-
171
- context "select" do
172
- it "does not block on super small precision intervals" do
173
- wait_interval = 1e-4
174
-
175
- expect do
176
- Timeout.timeout(2) do
177
- subject.select(wait_interval)
178
- end
179
- end.not_to raise_error
180
- end
181
-
182
- it "selects IO objects" do
183
- writer << "ohai"
184
- unready = IO.pipe.first
185
-
186
- reader_monitor = subject.register(reader, :r)
187
- unready_monitor = subject.register(unready, :r)
188
-
189
- selected = subject.select(0)
190
- expect(selected.size).to eq(1)
191
- expect(selected).to include reader_monitor
192
- expect(selected).not_to include unready_monitor
193
- end
194
-
195
- it "selects closed IO objects" do
196
- monitor = subject.register(reader, :r)
197
- expect(subject.select(0)).to be_nil
198
-
199
- thread = Thread.new { subject.select }
200
- Thread.pass while thread.status && thread.status != "sleep"
201
-
202
- writer.close
203
- selected = thread.value
204
- expect(selected).to include monitor
205
- end
206
-
207
- it "iterates across selected objects with a block" do
208
- readable1, writer = IO.pipe
209
- writer << "ohai"
210
-
211
- readable2, writer = IO.pipe
212
- writer << "ohai"
213
-
214
- unreadable = IO.pipe.first
215
-
216
- monitor1 = subject.register(readable1, :r)
217
- monitor2 = subject.register(readable2, :r)
218
- monitor3 = subject.register(unreadable, :r)
219
-
220
- readables = []
221
- result = subject.select { |monitor| readables << monitor }
222
- expect(result).to eq(2)
223
-
224
- expect(readables).to include monitor1
225
- expect(readables).to include monitor2
226
- expect(readables).not_to include monitor3
227
- end
228
-
229
- it "raises IOError if asked to select on a closed selector" do
230
- subject.close
231
-
232
- expect { subject.select(0) }.to raise_exception IOError
233
- end
234
- end
235
-
236
- it "closes" do
237
- subject.close
238
- expect(subject).to be_closed
239
- end
240
- end
data/spec/spec_helper.rb DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "coveralls"
4
- Coveralls.wear!
5
-
6
- require "nio"
7
- require "support/selectable_examples"
8
-
9
- RSpec.configure do |config|
10
- config.disable_monkey_patching!
11
-
12
- # Enable flags like --only-failures and --next-failure
13
- config.example_status_persistence_file_path = ".rspec_status"
14
-
15
- config.filter_run_when_matching :focus
16
-
17
- config.expect_with :rspec do |c|
18
- c.syntax = :expect
19
- end
20
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.shared_context NIO::Selector do
4
- let(:selector) {@selector = NIO::Selector.new}
5
-
6
- after(:each) do
7
- if defined?(@selector)
8
- @selector.close
9
- end
10
- end
11
- end
12
-
13
- RSpec.shared_context "an NIO selectable" do
14
- include_context NIO::Selector
15
-
16
- it "selects readable objects" do
17
- monitor = selector.register(readable_subject, :r)
18
- ready = selector.select(1)
19
- expect(ready).to be_an Enumerable
20
- expect(ready).to include monitor
21
- end
22
-
23
- it "does not select unreadable objects" do
24
- selector.register(unreadable_subject, :r)
25
- expect(selector.select(0)).to be_nil
26
- end
27
-
28
- it "selects writable objects" do
29
- monitor = selector.register(writable_subject, :w)
30
- ready = selector.select(1)
31
- expect(ready).to be_an Enumerable
32
- expect(ready).to include monitor
33
- end
34
-
35
- it "does not select unwritable objects" do
36
- selector.register(unwritable_subject, :w)
37
- ready = selector.select(0)
38
- expect(ready).to be_nil
39
- end
40
- end
41
-
42
- RSpec.shared_context "an NIO selectable stream" do |is_tls13|
43
- include_context NIO::Selector
44
-
45
- let(:stream) { pair.first }
46
- let(:peer) { pair.last }
47
-
48
- it "selects readable when the other end closes" do
49
- monitor = selector.register(stream, :r)
50
- unless is_tls13
51
- expect(selector.select(1)).to be_nil
52
- end
53
-
54
- peer.close
55
- # Wait and give the TCP session time to close
56
- expect(selector.select(0.1)).to include monitor
57
- end
58
- end
59
-
60
- RSpec.shared_context "an NIO bidirectional stream" do
61
- include_context NIO::Selector
62
-
63
- let(:stream) {pair.first}
64
- let(:peer) {pair.last}
65
-
66
- it "selects readable and writable" do
67
- selector.register(readable_subject, :rw)
68
-
69
- selector.select(1) do |monitor|
70
- expect(monitor.readiness).to eq(:rw)
71
- end
72
-
73
- readable_subject.close
74
- end
75
-
76
- it "keeps readiness after the selectable has been closed" do
77
- selector.register(readable_subject, :rw)
78
-
79
- selector.select(1) do |monitor|
80
- expect(monitor.readiness).to eq(:rw)
81
- readable_subject.close
82
- expect(monitor.readiness).to eq(:rw)
83
- end
84
- end
85
- end