nio4r 2.1.0 → 2.2.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 +5 -5
- data/.rubocop.yml +22 -2
- data/.travis.yml +3 -2
- data/CHANGES.md +43 -0
- data/Gemfile +2 -2
- data/Guardfile +1 -1
- data/README.md +3 -1
- data/Rakefile +1 -1
- data/appveyor.yml +27 -0
- data/ext/nio4r/bytebuffer.c +12 -4
- data/ext/nio4r/extconf.rb +15 -24
- data/ext/nio4r/monitor.c +2 -2
- data/ext/nio4r/org/nio4r/ByteBuffer.java +1 -3
- data/ext/nio4r/org/nio4r/Monitor.java +6 -0
- data/ext/nio4r/org/nio4r/Selector.java +18 -1
- data/ext/nio4r/selector.c +84 -9
- data/lib/nio.rb +3 -4
- data/lib/nio/bytebuffer.rb +11 -8
- data/lib/nio/monitor.rb +1 -1
- data/lib/nio/selector.rb +10 -1
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +1 -1
- data/spec/nio/acceptables_spec.rb +3 -3
- data/spec/nio/bytebuffer_spec.rb +8 -1
- data/spec/nio/monitor_spec.rb +1 -1
- data/spec/nio/selectables/pipe_spec.rb +7 -2
- data/spec/nio/selectables/ssl_socket_spec.rb +8 -9
- data/spec/nio/selectables/tcp_socket_spec.rb +2 -6
- data/spec/nio/selectables/udp_socket_spec.rb +15 -5
- data/spec/nio/selector_spec.rb +25 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/support/selectable_examples.rb +8 -3
- metadata +6 -6
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1b801ac687bb5098220b7d2b870f84d172cdcd0683c50175006196e7ab9f5021
|
4
|
+
data.tar.gz: 93301d044d3bb5fe7b7e9db9b1ab7d0b683770a22adda61b6e6ccb60bbe1a137
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 000f6a6ebe2acb74581d0c4976c0fc46ed2ae2baeaea6b18622c7b7105567e3734517429867e9b1bed9df5e8b320430d4dff505f98e817d9c6248eca6a7ccdba
|
7
|
+
data.tar.gz: 988ee869b08ea7d91c5383ee28c505db0e595eec35c98513b3468e1ecec66224894514e352db810cc1143b797c9c6af714ebd3d3eee90291f036e09ad4a28ccd
|
data/.rubocop.yml
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
+
TargetRubyVersion: 2.2
|
2
3
|
DisplayCopNames: true
|
3
4
|
|
4
5
|
#
|
@@ -18,6 +19,9 @@ Lint/Loop:
|
|
18
19
|
Metrics/AbcSize:
|
19
20
|
Max: 35
|
20
21
|
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Max: 128
|
24
|
+
|
21
25
|
Metrics/ClassLength:
|
22
26
|
Max: 128
|
23
27
|
|
@@ -34,12 +38,22 @@ Metrics/CyclomaticComplexity:
|
|
34
38
|
Metrics/PerceivedComplexity:
|
35
39
|
Max: 15
|
36
40
|
|
41
|
+
#
|
42
|
+
# Performance
|
43
|
+
#
|
44
|
+
|
45
|
+
Performance/RegexpMatch:
|
46
|
+
Enabled: false
|
47
|
+
|
37
48
|
#
|
38
49
|
# Style
|
39
50
|
#
|
40
51
|
|
41
|
-
Style/
|
42
|
-
|
52
|
+
Style/FormatStringToken:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Style/FrozenStringLiteralComment:
|
56
|
+
Enabled: true
|
43
57
|
|
44
58
|
Style/GlobalVars:
|
45
59
|
Enabled: false
|
@@ -50,5 +64,11 @@ Style/NumericPredicate:
|
|
50
64
|
Style/RescueModifier:
|
51
65
|
Enabled: false
|
52
66
|
|
67
|
+
Style/SafeNavigation:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/StringLiterals:
|
71
|
+
EnforcedStyle: double_quotes
|
72
|
+
|
53
73
|
Style/TrivialAccessors:
|
54
74
|
Enabled: false
|
data/.travis.yml
CHANGED
@@ -3,7 +3,7 @@ sudo: false
|
|
3
3
|
cache: bundler
|
4
4
|
|
5
5
|
before_install:
|
6
|
-
- gem update --system
|
6
|
+
- gem update --system
|
7
7
|
- gem --version
|
8
8
|
|
9
9
|
bundler_args: --without development
|
@@ -13,10 +13,11 @@ branches:
|
|
13
13
|
- master
|
14
14
|
|
15
15
|
rvm:
|
16
|
-
- jruby-9.1.
|
16
|
+
- jruby-9.1.15.0 # latest stable
|
17
17
|
- 2.2.7
|
18
18
|
- 2.3.4
|
19
19
|
- 2.4.1
|
20
|
+
- 2.5.0
|
20
21
|
- ruby-head
|
21
22
|
|
22
23
|
env:
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,41 @@
|
|
1
|
+
## 2.2.0 (2017-12-27)
|
2
|
+
|
3
|
+
* [#151](https://github.com/socketry/nio4r/pull/151)
|
4
|
+
`NIO::Selector`: Support for enumerating and configuring backend
|
5
|
+
([@tarcieri])
|
6
|
+
|
7
|
+
* [#153](https://github.com/socketry/nio4r/pull/153)
|
8
|
+
Fix builds on Windows
|
9
|
+
([@unak])
|
10
|
+
|
11
|
+
* [#157](https://github.com/socketry/nio4r/pull/157)
|
12
|
+
Windows / MinGW test failure - fix spec_helper.rb
|
13
|
+
([@MSP-Greg])
|
14
|
+
|
15
|
+
* [#162](https://github.com/socketry/nio4r/pull/162)
|
16
|
+
Don't build the C extension on Windows
|
17
|
+
([@larskanis])
|
18
|
+
|
19
|
+
* [#164](https://github.com/socketry/nio4r/pull/164)
|
20
|
+
Fix NIO::ByteBuffer leak
|
21
|
+
([@HoneyryderChuck])
|
22
|
+
|
23
|
+
* [#170](https://github.com/socketry/nio4r/pull/170)
|
24
|
+
Avoid CancelledKeyExceptions on JRuby
|
25
|
+
([@HoneyryderChuck])
|
26
|
+
|
27
|
+
* [#177](https://github.com/socketry/nio4r/pull/177)
|
28
|
+
Fix `NIO::ByteBuffer` string conversions on JRuby
|
29
|
+
([@tarcieri])
|
30
|
+
|
31
|
+
* [#179](https://github.com/socketry/nio4r/pull/179)
|
32
|
+
Fix argument error when running on ruby 2.5.0
|
33
|
+
([@tompng])
|
34
|
+
|
35
|
+
* [#180](https://github.com/socketry/nio4r/pull/180)
|
36
|
+
ext/nio4r/extconf.rb: check for port_event_t in port.h (fixes #178)
|
37
|
+
([@tarcieri])
|
38
|
+
|
1
39
|
## 2.1.0 (2017-05-28)
|
2
40
|
|
3
41
|
* [#130](https://github.com/socketry/nio4r/pull/130)
|
@@ -163,3 +201,8 @@
|
|
163
201
|
[@johnnyt]: https://github.com/johnnyt
|
164
202
|
[@UpeksheJay]: https://github.com/UpeksheJay
|
165
203
|
[@junaruga]: https://github.com/junaruga
|
204
|
+
[@unak]: https://github.com/unak
|
205
|
+
[@MSP-Greg]: https://github.com/MSP-Greg
|
206
|
+
[@larskanis]: https://github.com/larskanis
|
207
|
+
[@HoneyryderChuck]: https://github.com/HoneyryderChuck
|
208
|
+
[@tompng]: https://github.com/tompng
|
data/Gemfile
CHANGED
@@ -14,7 +14,7 @@ end
|
|
14
14
|
group :development, :test do
|
15
15
|
gem "coveralls", require: false
|
16
16
|
gem "rake-compiler", require: false
|
17
|
-
gem "rspec", "~> 3",
|
17
|
+
gem "rspec", "~> 3.7", require: false
|
18
18
|
gem "rspec-retry", require: false
|
19
|
-
gem "rubocop", "0.
|
19
|
+
gem "rubocop", "0.52.1", require: false
|
20
20
|
end
|
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# 
|
2
2
|
|
3
3
|
[](http://rubygems.org/gems/nio4r)
|
4
|
-
[](http://travis-ci.org/socketry/nio4r)
|
5
|
+
[](https://ci.appveyor.com/project/tarcieri/nio4r/branch/master)
|
5
6
|
[](https://codeclimate.com/github/socketry/nio4r)
|
6
7
|
[](https://coveralls.io/r/socketry/nio4r)
|
8
|
+
[](http://www.rubydoc.info/gems/nio4r/2.2.0)
|
7
9
|
[](https://github.com/socketry/nio4r/blob/master/LICENSE.txt)
|
8
10
|
|
9
11
|
_NOTE: This is the 2.x **stable** branch of nio4r. For the 1.x **legacy** branch,
|
data/Rakefile
CHANGED
data/appveyor.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
branches:
|
2
|
+
only:
|
3
|
+
- master
|
4
|
+
|
5
|
+
environment:
|
6
|
+
PATH: C:\Ruby%RUBY_VERSION%\DevKit\mingw\bin;C:\Ruby%RUBY_VERSION%\bin;C:\Ruby%RUBY_VERSION%\DevKit\bin;%PATH%
|
7
|
+
matrix:
|
8
|
+
- RUBY_VERSION: "24-x64"
|
9
|
+
- RUBY_VERSION: "23-x64"
|
10
|
+
- RUBY_VERSION: "23"
|
11
|
+
- RUBY_VERSION: "22"
|
12
|
+
|
13
|
+
install:
|
14
|
+
- SET RAKEOPT=-rdevkit
|
15
|
+
- ruby -v
|
16
|
+
- gem -v
|
17
|
+
- bundle -v
|
18
|
+
- bundle install
|
19
|
+
|
20
|
+
build: off
|
21
|
+
|
22
|
+
before_build:
|
23
|
+
- gem update --system
|
24
|
+
|
25
|
+
test_script:
|
26
|
+
- echo %PATH%
|
27
|
+
- bundle exec rake spec
|
data/ext/nio4r/bytebuffer.c
CHANGED
@@ -75,6 +75,7 @@ void Init_NIO_ByteBuffer()
|
|
75
75
|
static VALUE NIO_ByteBuffer_allocate(VALUE klass)
|
76
76
|
{
|
77
77
|
struct NIO_ByteBuffer *bytebuffer = (struct NIO_ByteBuffer *)xmalloc(sizeof(struct NIO_ByteBuffer));
|
78
|
+
bytebuffer->buffer = NULL;
|
78
79
|
return Data_Wrap_Struct(klass, NIO_ByteBuffer_gc_mark, NIO_ByteBuffer_free, bytebuffer);
|
79
80
|
}
|
80
81
|
|
@@ -84,6 +85,8 @@ static void NIO_ByteBuffer_gc_mark(struct NIO_ByteBuffer *buffer)
|
|
84
85
|
|
85
86
|
static void NIO_ByteBuffer_free(struct NIO_ByteBuffer *buffer)
|
86
87
|
{
|
88
|
+
if(buffer->buffer)
|
89
|
+
xfree(buffer->buffer);
|
87
90
|
xfree(buffer);
|
88
91
|
}
|
89
92
|
|
@@ -124,10 +127,11 @@ static VALUE NIO_ByteBuffer_get_position(VALUE self)
|
|
124
127
|
|
125
128
|
static VALUE NIO_ByteBuffer_set_position(VALUE self, VALUE new_position)
|
126
129
|
{
|
130
|
+
int pos;
|
127
131
|
struct NIO_ByteBuffer *buffer;
|
128
132
|
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
|
129
133
|
|
130
|
-
|
134
|
+
pos = NUM2INT(new_position);
|
131
135
|
|
132
136
|
if(pos < 0) {
|
133
137
|
rb_raise(rb_eArgError, "negative position given");
|
@@ -156,10 +160,11 @@ static VALUE NIO_ByteBuffer_get_limit(VALUE self)
|
|
156
160
|
|
157
161
|
static VALUE NIO_ByteBuffer_set_limit(VALUE self, VALUE new_limit)
|
158
162
|
{
|
163
|
+
int lim;
|
159
164
|
struct NIO_ByteBuffer *buffer;
|
160
165
|
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
|
161
166
|
|
162
|
-
|
167
|
+
lim = NUM2INT(new_limit);
|
163
168
|
|
164
169
|
if(lim < 0) {
|
165
170
|
rb_raise(rb_eArgError, "negative limit given");
|
@@ -237,10 +242,11 @@ static VALUE NIO_ByteBuffer_get(int argc, VALUE *argv, VALUE self)
|
|
237
242
|
|
238
243
|
static VALUE NIO_ByteBuffer_fetch(VALUE self, VALUE index)
|
239
244
|
{
|
245
|
+
int i;
|
240
246
|
struct NIO_ByteBuffer *buffer;
|
241
247
|
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
|
242
248
|
|
243
|
-
|
249
|
+
i = NUM2INT(index);
|
244
250
|
|
245
251
|
if(i < 0) {
|
246
252
|
rb_raise(rb_eArgError, "negative index given");
|
@@ -255,10 +261,12 @@ static VALUE NIO_ByteBuffer_fetch(VALUE self, VALUE index)
|
|
255
261
|
|
256
262
|
static VALUE NIO_ByteBuffer_put(VALUE self, VALUE string)
|
257
263
|
{
|
264
|
+
long length;
|
258
265
|
struct NIO_ByteBuffer *buffer;
|
259
266
|
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
|
260
267
|
|
261
|
-
|
268
|
+
StringValue(string);
|
269
|
+
length = RSTRING_LEN(string);
|
262
270
|
|
263
271
|
if(length > buffer->limit - buffer->position) {
|
264
272
|
rb_raise(cNIO_ByteBuffer_OverflowError, "buffer is full");
|
data/ext/nio4r/extconf.rb
CHANGED
@@ -1,35 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
|
5
|
-
have_header("unistd.h")
|
6
|
-
|
7
|
-
$defs << "-DEV_USE_SELECT" if have_header("sys/select.h")
|
3
|
+
require "rubygems"
|
8
4
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
$defs << "-DEV_USE_KQUEUE"
|
5
|
+
# Write a dummy Makefile on Windows because we use the pure Ruby implementation there
|
6
|
+
if Gem.win_platform?
|
7
|
+
File.write("Makefile", "all install::\n")
|
8
|
+
File.write("nio4r_ext.so", "")
|
9
|
+
exit
|
15
10
|
end
|
16
11
|
|
17
|
-
|
12
|
+
require "mkmf"
|
18
13
|
|
14
|
+
have_header("unistd.h")
|
15
|
+
|
16
|
+
$defs << "-DEV_USE_SELECT" if have_header("sys/select.h")
|
17
|
+
$defs << "-DEV_USE_POLL" if have_type("port_event_t", "poll.h")
|
18
|
+
$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h")
|
19
|
+
$defs << "-DEV_USE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
|
20
|
+
$defs << "-DEV_USE_PORT" if have_type("port_event_t", "port.h")
|
19
21
|
$defs << "-DHAVE_SYS_RESOURCE_H" if have_header("sys/resource.h")
|
20
22
|
|
21
|
-
CONFIG["optflags"] << " -fno-strict-aliasing"
|
23
|
+
CONFIG["optflags"] << " -fno-strict-aliasing" unless RUBY_PLATFORM =~ /mswin/
|
22
24
|
|
23
25
|
dir_config "nio4r_ext"
|
24
26
|
create_makefile "nio4r_ext"
|
25
|
-
|
26
|
-
# win32 needs to link in "just the right order" for some reason or
|
27
|
-
# ioctlsocket will be mapped to an [inverted] ruby specific version.
|
28
|
-
if RUBY_PLATFORM =~ /mingw|win32/
|
29
|
-
makefile_contents = File.read "Makefile"
|
30
|
-
|
31
|
-
makefile_contents.gsub! "DLDFLAGS = ", "DLDFLAGS = -export-all "
|
32
|
-
|
33
|
-
makefile_contents.gsub! "LIBS = $(LIBRUBYARG_SHARED)", "LIBS = -lws2_32 $(LIBRUBYARG_SHARED)"
|
34
|
-
File.open("Makefile", "w") { |f| f.write makefile_contents }
|
35
|
-
end
|
data/ext/nio4r/monitor.c
CHANGED
@@ -92,7 +92,7 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
|
|
92
92
|
monitor->interests = EV_READ | EV_WRITE;
|
93
93
|
} else {
|
94
94
|
rb_raise(rb_eArgError, "invalid event type %s (must be :r, :w, or :rw)",
|
95
|
-
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0
|
95
|
+
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0)));
|
96
96
|
}
|
97
97
|
|
98
98
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
@@ -258,7 +258,7 @@ static int NIO_Monitor_symbol2interest(VALUE interests)
|
|
258
258
|
return EV_READ | EV_WRITE;
|
259
259
|
} else {
|
260
260
|
rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
|
261
|
-
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0
|
261
|
+
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0)));
|
262
262
|
}
|
263
263
|
}
|
264
264
|
|
@@ -163,10 +163,8 @@ public class ByteBuffer extends RubyObject {
|
|
163
163
|
|
164
164
|
@JRubyMethod(name = "<<")
|
165
165
|
public IRubyObject put(ThreadContext context, IRubyObject str) {
|
166
|
-
String string = str.asJavaString();
|
167
|
-
|
168
166
|
try {
|
169
|
-
this.byteBuffer.put(
|
167
|
+
this.byteBuffer.put(str.convertToString().getByteList().bytes());
|
170
168
|
} catch(BufferOverflowException e) {
|
171
169
|
throw ByteBuffer.newOverflowError(context, "buffer is full");
|
172
170
|
}
|
@@ -102,12 +102,16 @@ public class Monitor extends RubyObject {
|
|
102
102
|
|
103
103
|
@JRubyMethod
|
104
104
|
public IRubyObject readiness(ThreadContext context) {
|
105
|
+
if(!key.isValid())
|
106
|
+
return this.interests;
|
105
107
|
return Nio4r.interestOpsToSymbol(context.getRuntime(), key.readyOps());
|
106
108
|
}
|
107
109
|
|
108
110
|
@JRubyMethod(name = "readable?")
|
109
111
|
public IRubyObject isReadable(ThreadContext context) {
|
110
112
|
Ruby runtime = context.getRuntime();
|
113
|
+
if (!this.key.isValid())
|
114
|
+
return runtime.getTrue();
|
111
115
|
int readyOps = this.key.readyOps();
|
112
116
|
|
113
117
|
if((readyOps & SelectionKey.OP_READ) != 0 || (readyOps & SelectionKey.OP_ACCEPT) != 0) {
|
@@ -120,6 +124,8 @@ public class Monitor extends RubyObject {
|
|
120
124
|
@JRubyMethod(name = {"writable?", "writeable?"})
|
121
125
|
public IRubyObject writable(ThreadContext context) {
|
122
126
|
Ruby runtime = context.getRuntime();
|
127
|
+
if (!this.key.isValid())
|
128
|
+
return runtime.getTrue();
|
123
129
|
int readyOps = this.key.readyOps();
|
124
130
|
|
125
131
|
if((readyOps & SelectionKey.OP_WRITE) != 0 || (readyOps & SelectionKey.OP_CONNECT) != 0) {
|
@@ -7,6 +7,7 @@ import java.io.IOException;
|
|
7
7
|
import java.nio.channels.Channel;
|
8
8
|
import java.nio.channels.SelectableChannel;
|
9
9
|
import java.nio.channels.SelectionKey;
|
10
|
+
import java.nio.channels.CancelledKeyException;
|
10
11
|
|
11
12
|
import org.jruby.Ruby;
|
12
13
|
import org.jruby.RubyArray;
|
@@ -30,8 +31,23 @@ public class Selector extends RubyObject {
|
|
30
31
|
super(ruby, rubyClass);
|
31
32
|
}
|
32
33
|
|
34
|
+
@JRubyMethod(meta = true)
|
35
|
+
public static IRubyObject backends(ThreadContext context, IRubyObject self) {
|
36
|
+
return context.runtime.newArray(context.runtime.newSymbol("java"));
|
37
|
+
}
|
38
|
+
|
33
39
|
@JRubyMethod
|
34
40
|
public IRubyObject initialize(ThreadContext context) {
|
41
|
+
initialize(context, context.runtime.newSymbol("java"));
|
42
|
+
return context.nil;
|
43
|
+
}
|
44
|
+
|
45
|
+
@JRubyMethod
|
46
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject backend) {
|
47
|
+
if(backend != context.runtime.newSymbol("java")) {
|
48
|
+
throw context.runtime.newArgumentError(":java is the only supported backend");
|
49
|
+
}
|
50
|
+
|
35
51
|
this.cancelledKeys = new HashMap<SelectableChannel,SelectionKey>();
|
36
52
|
this.wakeupFired = false;
|
37
53
|
|
@@ -193,6 +209,7 @@ public class Selector extends RubyObject {
|
|
193
209
|
while(selectedKeys.hasNext()) {
|
194
210
|
SelectionKey key = (SelectionKey)selectedKeys.next();
|
195
211
|
processKey(key);
|
212
|
+
|
196
213
|
selectedKeys.remove();
|
197
214
|
|
198
215
|
if(block.isGiven()) {
|
@@ -254,7 +271,7 @@ public class Selector extends RubyObject {
|
|
254
271
|
// Remove connect interest from connected sockets
|
255
272
|
// See: http://stackoverflow.com/questions/204186/java-nio-select-returns-without-selected-keys-why
|
256
273
|
private void processKey(SelectionKey key) {
|
257
|
-
if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
|
274
|
+
if(key.isValid() && (key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
|
258
275
|
int interestOps = key.interestOps();
|
259
276
|
|
260
277
|
interestOps &= ~SelectionKey.OP_CONNECT;
|
data/ext/nio4r/selector.c
CHANGED
@@ -27,8 +27,11 @@ static void NIO_Selector_mark(struct NIO_Selector *loop);
|
|
27
27
|
static void NIO_Selector_shutdown(struct NIO_Selector *selector);
|
28
28
|
static void NIO_Selector_free(struct NIO_Selector *loop);
|
29
29
|
|
30
|
-
/*
|
31
|
-
static VALUE
|
30
|
+
/* Class methods */
|
31
|
+
static VALUE NIO_Selector_supported_backends(VALUE klass);
|
32
|
+
|
33
|
+
/* Instance methods */
|
34
|
+
static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self);
|
32
35
|
static VALUE NIO_Selector_backend(VALUE self);
|
33
36
|
static VALUE NIO_Selector_register(VALUE self, VALUE selectable, VALUE interest);
|
34
37
|
static VALUE NIO_Selector_deregister(VALUE self, VALUE io);
|
@@ -65,7 +68,8 @@ void Init_NIO_Selector()
|
|
65
68
|
cNIO_Selector = rb_define_class_under(mNIO, "Selector", rb_cObject);
|
66
69
|
rb_define_alloc_func(cNIO_Selector, NIO_Selector_allocate);
|
67
70
|
|
68
|
-
|
71
|
+
rb_define_singleton_method(cNIO_Selector, "backends", NIO_Selector_supported_backends, 0);
|
72
|
+
rb_define_method(cNIO_Selector, "initialize", NIO_Selector_initialize, -1);
|
69
73
|
rb_define_method(cNIO_Selector, "backend", NIO_Selector_backend, 0);
|
70
74
|
rb_define_method(cNIO_Selector, "register", NIO_Selector_register, 2);
|
71
75
|
rb_define_method(cNIO_Selector, "deregister", NIO_Selector_deregister, 1);
|
@@ -102,7 +106,9 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
102
106
|
}
|
103
107
|
|
104
108
|
selector = (struct NIO_Selector *)xmalloc(sizeof(struct NIO_Selector));
|
105
|
-
|
109
|
+
|
110
|
+
/* Defer initializing the loop to #initialize */
|
111
|
+
selector->ev_loop = 0;
|
106
112
|
|
107
113
|
ev_init(&selector->timer, NIO_Selector_timeout_callback);
|
108
114
|
|
@@ -112,8 +118,6 @@ static VALUE NIO_Selector_allocate(VALUE klass)
|
|
112
118
|
ev_io_init(&selector->wakeup, NIO_Selector_wakeup_callback, selector->wakeup_reader, EV_READ);
|
113
119
|
selector->wakeup.data = (void *)selector;
|
114
120
|
|
115
|
-
ev_io_start(selector->ev_loop, &selector->wakeup);
|
116
|
-
|
117
121
|
selector->closed = selector->selecting = selector->wakeup_fired = selector->ready_count = 0;
|
118
122
|
selector->ready_array = Qnil;
|
119
123
|
|
@@ -154,12 +158,83 @@ static void NIO_Selector_free(struct NIO_Selector *selector)
|
|
154
158
|
xfree(selector);
|
155
159
|
}
|
156
160
|
|
161
|
+
/* Return an array of symbols for supported backends */
|
162
|
+
static VALUE NIO_Selector_supported_backends(VALUE klass) {
|
163
|
+
unsigned int backends = ev_supported_backends();
|
164
|
+
VALUE result = rb_ary_new();
|
165
|
+
|
166
|
+
if(backends & EVBACKEND_EPOLL) {
|
167
|
+
rb_ary_push(result, ID2SYM(rb_intern("epoll")));
|
168
|
+
}
|
169
|
+
|
170
|
+
if(backends & EVBACKEND_POLL) {
|
171
|
+
rb_ary_push(result, ID2SYM(rb_intern("poll")));
|
172
|
+
}
|
173
|
+
|
174
|
+
if(backends & EVBACKEND_KQUEUE) {
|
175
|
+
rb_ary_push(result, ID2SYM(rb_intern("kqueue")));
|
176
|
+
}
|
177
|
+
|
178
|
+
if(backends & EVBACKEND_SELECT) {
|
179
|
+
rb_ary_push(result, ID2SYM(rb_intern("select")));
|
180
|
+
}
|
181
|
+
|
182
|
+
if(backends & EVBACKEND_PORT) {
|
183
|
+
rb_ary_push(result, ID2SYM(rb_intern("port")));
|
184
|
+
}
|
185
|
+
|
186
|
+
return result;
|
187
|
+
}
|
188
|
+
|
157
189
|
/* Create a new selector. This is more or less the pure Ruby version
|
158
190
|
translated into an MRI cext */
|
159
|
-
static VALUE NIO_Selector_initialize(VALUE self)
|
191
|
+
static VALUE NIO_Selector_initialize(int argc, VALUE *argv, VALUE self)
|
160
192
|
{
|
193
|
+
ID backend_id;
|
194
|
+
VALUE backend;
|
161
195
|
VALUE lock;
|
162
196
|
|
197
|
+
struct NIO_Selector *selector;
|
198
|
+
unsigned int flags = 0;
|
199
|
+
|
200
|
+
Data_Get_Struct(self, struct NIO_Selector, selector);
|
201
|
+
|
202
|
+
rb_scan_args(argc, argv, "01", &backend);
|
203
|
+
|
204
|
+
if(backend != Qnil) {
|
205
|
+
if(!rb_ary_includes(NIO_Selector_supported_backends(CLASS_OF(self)), backend)) {
|
206
|
+
rb_raise(rb_eArgError, "unsupported backend: %s",
|
207
|
+
RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
208
|
+
}
|
209
|
+
|
210
|
+
backend_id = SYM2ID(backend);
|
211
|
+
|
212
|
+
if(backend_id == rb_intern("epoll")) {
|
213
|
+
flags = EVBACKEND_EPOLL;
|
214
|
+
} else if(backend_id == rb_intern("poll")) {
|
215
|
+
flags = EVBACKEND_POLL;
|
216
|
+
} else if(backend_id == rb_intern("kqueue")) {
|
217
|
+
flags = EVBACKEND_KQUEUE;
|
218
|
+
} else if(backend_id == rb_intern("select")) {
|
219
|
+
flags = EVBACKEND_SELECT;
|
220
|
+
} else if(backend_id == rb_intern("port")) {
|
221
|
+
flags = EVBACKEND_PORT;
|
222
|
+
} else {
|
223
|
+
rb_raise(rb_eArgError, "unsupported backend: %s",
|
224
|
+
RSTRING_PTR(rb_funcall(backend, rb_intern("inspect"), 0)));
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
/* Ensure the selector loop has not yet been initialized */
|
229
|
+
assert(!selector->ev_loop);
|
230
|
+
|
231
|
+
selector->ev_loop = ev_loop_new(flags);
|
232
|
+
if(!selector->ev_loop) {
|
233
|
+
rb_raise(rb_eIOError, "error initializing event loop");
|
234
|
+
}
|
235
|
+
|
236
|
+
ev_io_start(selector->ev_loop, &selector->wakeup);
|
237
|
+
|
163
238
|
rb_ivar_set(self, rb_intern("selectables"), rb_hash_new());
|
164
239
|
rb_ivar_set(self, rb_intern("lock_holder"), Qnil);
|
165
240
|
|
@@ -204,7 +279,7 @@ static VALUE NIO_Selector_synchronize(VALUE self, VALUE (*func)(VALUE *args), VA
|
|
204
279
|
|
205
280
|
if(lock_holder != current_thread) {
|
206
281
|
lock = rb_ivar_get(self, rb_intern("lock"));
|
207
|
-
rb_funcall(lock, rb_intern("lock"), 0
|
282
|
+
rb_funcall(lock, rb_intern("lock"), 0);
|
208
283
|
rb_ivar_set(self, rb_intern("lock_holder"), current_thread);
|
209
284
|
|
210
285
|
/* We've acquired the lock, so ensure we unlock it */
|
@@ -223,7 +298,7 @@ static VALUE NIO_Selector_unlock(VALUE self)
|
|
223
298
|
rb_ivar_set(self, rb_intern("lock_holder"), Qnil);
|
224
299
|
|
225
300
|
lock = rb_ivar_get(self, rb_intern("lock"));
|
226
|
-
rb_funcall(lock, rb_intern("unlock"), 0
|
301
|
+
rb_funcall(lock, rb_intern("unlock"), 0);
|
227
302
|
|
228
303
|
return Qnil;
|
229
304
|
}
|
data/lib/nio.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "thread"
|
4
3
|
require "socket"
|
5
4
|
require "nio/version"
|
6
5
|
|
@@ -19,7 +18,7 @@ if ENV["NIO4R_PURE"] == "true" || (Gem.win_platform? && !defined?(JRUBY_VERSION)
|
|
19
18
|
require "nio/monitor"
|
20
19
|
require "nio/selector"
|
21
20
|
require "nio/bytebuffer"
|
22
|
-
NIO::ENGINE = "ruby"
|
21
|
+
NIO::ENGINE = "ruby".freeze
|
23
22
|
else
|
24
23
|
require "nio4r_ext"
|
25
24
|
|
@@ -27,8 +26,8 @@ else
|
|
27
26
|
require "java"
|
28
27
|
require "jruby"
|
29
28
|
org.nio4r.Nio4r.new.load(JRuby.runtime, false)
|
30
|
-
NIO::ENGINE = "java"
|
29
|
+
NIO::ENGINE = "java".freeze
|
31
30
|
else
|
32
|
-
NIO::ENGINE = "libev"
|
31
|
+
NIO::ENGINE = "libev".freeze
|
33
32
|
end
|
34
33
|
end
|
data/lib/nio/bytebuffer.rb
CHANGED
@@ -48,10 +48,8 @@ module NIO
|
|
48
48
|
raise ArgumentError, "negative position given" if new_position < 0
|
49
49
|
raise ArgumentError, "specified position exceeds capacity" if new_position > @capacity
|
50
50
|
|
51
|
+
@mark = nil if @mark && @mark > new_position
|
51
52
|
@position = new_position
|
52
|
-
@mark = nil if @mark && @mark > @position
|
53
|
-
|
54
|
-
new_position
|
55
53
|
end
|
56
54
|
|
57
55
|
# Set the limit to the given value. New limit must be less than capacity.
|
@@ -65,11 +63,9 @@ module NIO
|
|
65
63
|
raise ArgumentError, "negative limit given" if new_limit < 0
|
66
64
|
raise ArgumentError, "specified limit exceeds capacity" if new_limit > @capacity
|
67
65
|
|
66
|
+
@position = new_limit if @position > new_limit
|
67
|
+
@mark = nil if @mark && @mark > new_limit
|
68
68
|
@limit = new_limit
|
69
|
-
@position = new_limit if @position > @limit
|
70
|
-
@mark = nil if @mark && @mark > @limit
|
71
|
-
|
72
|
-
new_limit
|
73
69
|
end
|
74
70
|
|
75
71
|
# Number of bytes remaining in the buffer before the limit
|
@@ -115,15 +111,22 @@ module NIO
|
|
115
111
|
|
116
112
|
# Add a String to the buffer
|
117
113
|
#
|
114
|
+
# @param str [#to_str] data to add to the buffer
|
115
|
+
#
|
116
|
+
# @raise [TypeError] given a non-string type
|
118
117
|
# @raise [NIO::ByteBuffer::OverflowError] buffer is full
|
119
118
|
#
|
120
119
|
# @return [self]
|
121
|
-
def
|
120
|
+
def put(str)
|
121
|
+
raise TypeError, "expected String, got #{str.class}" unless str.respond_to?(:to_str)
|
122
|
+
str = str.to_str
|
123
|
+
|
122
124
|
raise OverflowError, "buffer is full" if str.length > @limit - @position
|
123
125
|
@buffer[@position...str.length] = str
|
124
126
|
@position += str.length
|
125
127
|
self
|
126
128
|
end
|
129
|
+
alias << put
|
127
130
|
|
128
131
|
# Perform a non-blocking read from the given IO object into the buffer
|
129
132
|
# Reads as much data as is immediately available and returns
|
data/lib/nio/monitor.rb
CHANGED
@@ -31,7 +31,7 @@ module NIO
|
|
31
31
|
# @return [Symbol] new interests
|
32
32
|
def interests=(interests)
|
33
33
|
raise EOFError, "monitor is closed" if closed?
|
34
|
-
raise ArgumentError, "bad interests: #{interests}" unless [
|
34
|
+
raise ArgumentError, "bad interests: #{interests}" unless %i[r w rw].include?(interests)
|
35
35
|
|
36
36
|
@interests = interests
|
37
37
|
end
|
data/lib/nio/selector.rb
CHANGED
@@ -5,8 +5,17 @@ require "set"
|
|
5
5
|
module NIO
|
6
6
|
# Selectors monitor IO objects for events of interest
|
7
7
|
class Selector
|
8
|
+
# Return supported backends as symbols
|
9
|
+
#
|
10
|
+
# See `#backend` method definition for all possible backends
|
11
|
+
def self.backends
|
12
|
+
[:ruby]
|
13
|
+
end
|
14
|
+
|
8
15
|
# Create a new NIO::Selector
|
9
|
-
def initialize
|
16
|
+
def initialize(backend = :ruby)
|
17
|
+
raise ArgumentError, "unsupported backend: #{backend}" unless backend == :ruby
|
18
|
+
|
10
19
|
@selectables = {}
|
11
20
|
@lock = Mutex.new
|
12
21
|
|
data/lib/nio/version.rb
CHANGED
data/nio4r.gemspec
CHANGED
@@ -20,13 +20,13 @@ RSpec.describe "NIO acceptables" do
|
|
20
20
|
let(:port) { next_available_tcp_port }
|
21
21
|
|
22
22
|
let :acceptable_subject do
|
23
|
-
server = TCPServer.new("
|
24
|
-
TCPSocket.open("
|
23
|
+
server = TCPServer.new("127.0.0.1", port)
|
24
|
+
TCPSocket.open("127.0.0.1", port)
|
25
25
|
server
|
26
26
|
end
|
27
27
|
|
28
28
|
let :unacceptable_subject do
|
29
|
-
TCPServer.new("
|
29
|
+
TCPServer.new("127.0.0.1", port + 1)
|
30
30
|
end
|
31
31
|
|
32
32
|
it_behaves_like "an NIO acceptable"
|
data/spec/nio/bytebuffer_spec.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
5
6
|
RSpec.describe NIO::ByteBuffer do
|
6
7
|
let(:capacity) { 256 }
|
7
8
|
let(:example_string) { "Testing 1 2 3..." }
|
@@ -179,6 +180,11 @@ RSpec.describe NIO::ByteBuffer do
|
|
179
180
|
expect(bytebuffer.limit).to eq capacity
|
180
181
|
end
|
181
182
|
|
183
|
+
it "raises TypeError if given a non-String type" do
|
184
|
+
expect { bytebuffer << 42 }.to raise_error(TypeError)
|
185
|
+
expect { bytebuffer << nil }.to raise_error(TypeError)
|
186
|
+
end
|
187
|
+
|
182
188
|
it "raises NIO::ByteBuffer::OverflowError if the buffer is full" do
|
183
189
|
bytebuffer << "X" * (capacity - 1)
|
184
190
|
expect { bytebuffer << "X" }.not_to raise_error
|
@@ -281,7 +287,7 @@ RSpec.describe NIO::ByteBuffer do
|
|
281
287
|
end
|
282
288
|
|
283
289
|
context "I/O" do
|
284
|
-
let(:addr) { "
|
290
|
+
let(:addr) { "127.0.0.1" }
|
285
291
|
let(:port) { next_available_tcp_port }
|
286
292
|
let(:server) { TCPServer.new(addr, port) }
|
287
293
|
let(:client) { TCPSocket.new(addr, port) }
|
@@ -347,3 +353,4 @@ RSpec.describe NIO::ByteBuffer do
|
|
347
353
|
end
|
348
354
|
end
|
349
355
|
end
|
356
|
+
# rubocop:enable Metrics/BlockLength
|
data/spec/nio/monitor_spec.rb
CHANGED
@@ -27,12 +27,17 @@ RSpec.describe "IO.pipe" do
|
|
27
27
|
# will throw EAGAIN if there is too little space to write the string
|
28
28
|
# TODO: Use FFI to lookup the platform-specific size of PIPE_BUF
|
29
29
|
str = "JUNK IN THE TUBES" * 10_000
|
30
|
+
cntr = 0
|
30
31
|
begin
|
31
32
|
pipe.write_nonblock str
|
32
|
-
|
33
|
+
cntr += 1
|
34
|
+
t = select [], [pipe], [], 0
|
33
35
|
rescue Errno::EPIPE
|
34
36
|
break
|
35
|
-
|
37
|
+
rescue IO::EWOULDBLOCKWaitWritable
|
38
|
+
skip "windows - can't test due to 'select' not showing correct status"
|
39
|
+
break
|
40
|
+
end while t && t[1].include?(pipe) && cntr < 20
|
36
41
|
|
37
42
|
pipe
|
38
43
|
end
|
@@ -4,23 +4,23 @@ require "spec_helper"
|
|
4
4
|
require "openssl"
|
5
5
|
|
6
6
|
RSpec.describe OpenSSL::SSL::SSLSocket do
|
7
|
-
let(:addr) { "
|
7
|
+
let(:addr) { "127.0.0.1" }
|
8
8
|
let(:port) { next_available_tcp_port }
|
9
9
|
|
10
10
|
let(:ssl_key) { OpenSSL::PKey::RSA.new(1024) }
|
11
11
|
|
12
12
|
let(:ssl_cert) do
|
13
|
-
name = OpenSSL::X509::Name.new([%w
|
13
|
+
name = OpenSSL::X509::Name.new([%w[CN 127.0.0.1]])
|
14
14
|
OpenSSL::X509::Certificate.new.tap do |cert|
|
15
15
|
cert.version = 2
|
16
16
|
cert.serial = 1
|
17
17
|
cert.issuer = name
|
18
18
|
cert.subject = name
|
19
19
|
cert.not_before = Time.now
|
20
|
-
cert.not_after = Time.now + (
|
20
|
+
cert.not_after = Time.now + (7 * 24 * 60 * 60)
|
21
21
|
cert.public_key = ssl_key.public_key
|
22
22
|
|
23
|
-
cert.sign(ssl_key, OpenSSL::Digest::
|
23
|
+
cert.sign(ssl_key, OpenSSL::Digest::SHA256.new)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -111,14 +111,15 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
111
111
|
ssl_peer.accept
|
112
112
|
thread.join
|
113
113
|
|
114
|
+
cntr = 0
|
114
115
|
begin
|
115
|
-
_, writers = select [], [ssl_client], [], 0
|
116
116
|
count = ssl_client.write_nonblock "X" * 1024
|
117
117
|
expect(count).not_to eq(0)
|
118
|
+
cntr += 1
|
119
|
+
t = select [], [ssl_client], [], 0
|
118
120
|
rescue IO::WaitReadable, IO::WaitWritable
|
119
121
|
pending "SSL will report writable but not accept writes"
|
120
|
-
|
121
|
-
end while writers && writers.include?(ssl_client)
|
122
|
+
end while t && t[1].include?(ssl_client) && cntr < 30
|
122
123
|
|
123
124
|
# I think the kernel might manage to drain its buffer a bit even after
|
124
125
|
# the socket first goes unwritable. Attempt to sleep past this and then
|
@@ -141,8 +142,6 @@ RSpec.describe OpenSSL::SSL::SSLSocket do
|
|
141
142
|
end
|
142
143
|
|
143
144
|
let :pair do
|
144
|
-
pending "figure out why newly created sockets are selecting readable immediately"
|
145
|
-
|
146
145
|
server = TCPServer.new(addr, port)
|
147
146
|
client = TCPSocket.new(addr, port)
|
148
147
|
peer = server.accept
|
@@ -19,9 +19,7 @@ RSpec.describe TCPSocket do
|
|
19
19
|
sock = TCPSocket.new(addr, port)
|
20
20
|
|
21
21
|
# Sanity check to make sure we actually produced an unreadable socket
|
22
|
-
if select([sock], [], [], 0)
|
23
|
-
pending "Failed to produce an unreadable socket"
|
24
|
-
end
|
22
|
+
pending "Failed to produce an unreadable socket" if select([sock], [], [], 0)
|
25
23
|
|
26
24
|
sock
|
27
25
|
end
|
@@ -57,9 +55,7 @@ RSpec.describe TCPSocket do
|
|
57
55
|
end
|
58
56
|
|
59
57
|
# Sanity check to make sure we actually produced an unwritable socket
|
60
|
-
if select([], [sock], [], 0)
|
61
|
-
pending "Failed to produce an unwritable socket"
|
62
|
-
end
|
58
|
+
pending "Failed to produce an unwritable socket" if select([], [sock], [], 0)
|
63
59
|
|
64
60
|
sock
|
65
61
|
end
|
@@ -2,27 +2,37 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
RSpec.describe UDPSocket do
|
5
|
+
RSpec.describe UDPSocket, if: !defined?(JRUBY_VERSION) do
|
6
6
|
let(:udp_port) { 23_456 }
|
7
7
|
|
8
8
|
let :readable_subject do
|
9
9
|
sock = UDPSocket.new
|
10
|
-
sock.bind("
|
10
|
+
sock.bind("127.0.0.1", udp_port)
|
11
11
|
|
12
12
|
peer = UDPSocket.new
|
13
|
-
peer.send("hi there", 0, "
|
13
|
+
peer.send("hi there", 0, "127.0.0.1", udp_port)
|
14
14
|
|
15
15
|
sock
|
16
16
|
end
|
17
17
|
|
18
18
|
let :unreadable_subject do
|
19
19
|
sock = UDPSocket.new
|
20
|
-
sock.bind("
|
20
|
+
sock.bind("127.0.0.1", udp_port + 1)
|
21
21
|
sock
|
22
22
|
end
|
23
23
|
|
24
24
|
let :writable_subject do
|
25
|
-
|
25
|
+
peer = UDPSocket.new
|
26
|
+
peer.connect "127.0.0.1", udp_port
|
27
|
+
cntr = 0
|
28
|
+
begin
|
29
|
+
peer.send("X" * 1024, 0)
|
30
|
+
cntr += 1
|
31
|
+
t = select [], [peer], [], 0
|
32
|
+
rescue Errno::ECONNREFUSED => ex
|
33
|
+
skip "Couln't make writable UDPSocket subject: #{ex.class}: #{ex}"
|
34
|
+
end while t && t[1].include?(peer) && cntr < 5
|
35
|
+
peer
|
26
36
|
end
|
27
37
|
|
28
38
|
let :unwritable_subject do
|
data/spec/nio/selector_spec.rb
CHANGED
@@ -8,11 +8,35 @@ require "timeout"
|
|
8
8
|
# the tests
|
9
9
|
TIMEOUT_PRECISION = 0.1
|
10
10
|
|
11
|
+
# rubocop:disable Metrics/BlockLength
|
11
12
|
RSpec.describe NIO::Selector do
|
12
13
|
let(:pair) { IO.pipe }
|
13
14
|
let(:reader) { pair.first }
|
14
15
|
let(:writer) { pair.last }
|
15
16
|
|
17
|
+
context ".backends" do
|
18
|
+
it "knows all supported backends" do
|
19
|
+
expect(described_class.backends).to be_a Array
|
20
|
+
expect(described_class.backends.first).to be_a Symbol
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "#initialize" do
|
25
|
+
it "allows explicitly specifying a backend" do
|
26
|
+
backend = described_class.backends.first
|
27
|
+
selector = described_class.new(backend)
|
28
|
+
expect(selector.backend).to eq 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
|
+
|
16
40
|
context "backend" do
|
17
41
|
it "knows its backend" do
|
18
42
|
expect(subject.backend).to be_a Symbol
|
@@ -191,3 +215,4 @@ RSpec.describe NIO::Selector do
|
|
191
215
|
expect(subject).to be_closed
|
192
216
|
end
|
193
217
|
end
|
218
|
+
# rubocop:enable Metrics/BlockLength
|
data/spec/spec_helper.rb
CHANGED
@@ -20,8 +20,8 @@ def next_available_tcp_port
|
|
20
20
|
$current_tcp_port += 1
|
21
21
|
|
22
22
|
begin
|
23
|
-
sock = Timeout.timeout(
|
24
|
-
rescue Errno::ECONNREFUSED
|
23
|
+
sock = Timeout.timeout(0.5) { TCPSocket.new("127.0.0.1", $current_tcp_port) }
|
24
|
+
rescue Errno::ECONNREFUSED, Timeout::Error
|
25
25
|
break $current_tcp_port
|
26
26
|
end
|
27
27
|
|
@@ -34,9 +34,6 @@ RSpec.shared_context "an NIO selectable stream" do
|
|
34
34
|
let(:peer) { pair.last }
|
35
35
|
|
36
36
|
it "selects readable when the other end closes" do
|
37
|
-
# hax: this test is broken for OpenSSL sockets
|
38
|
-
skip "broken for SSL ;_;" if peer.is_a? OpenSSL::SSL::SSLSocket
|
39
|
-
|
40
37
|
monitor = selector.register(stream, :r)
|
41
38
|
expect(selector.select(0)).to be_nil
|
42
39
|
|
@@ -57,4 +54,12 @@ RSpec.shared_context "an NIO bidirectional stream" do
|
|
57
54
|
expect(m.readiness).to eq(:rw)
|
58
55
|
end
|
59
56
|
end
|
57
|
+
it "keeps readiness after the selectable has been closed" do
|
58
|
+
selector.register(readable_subject, :rw)
|
59
|
+
selector.select(0) do |m|
|
60
|
+
expect(m.readiness).to eq(:rw)
|
61
|
+
readable_subject.close
|
62
|
+
expect(m.readiness).to eq(:rw)
|
63
|
+
end
|
64
|
+
end
|
60
65
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nio4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -50,7 +50,6 @@ files:
|
|
50
50
|
- ".gitignore"
|
51
51
|
- ".rspec"
|
52
52
|
- ".rubocop.yml"
|
53
|
-
- ".ruby-version"
|
54
53
|
- ".travis.yml"
|
55
54
|
- CHANGES.md
|
56
55
|
- Gemfile
|
@@ -58,6 +57,7 @@ files:
|
|
58
57
|
- LICENSE.txt
|
59
58
|
- README.md
|
60
59
|
- Rakefile
|
60
|
+
- appveyor.yml
|
61
61
|
- examples/echo_server.rb
|
62
62
|
- ext/libev/Changes
|
63
63
|
- ext/libev/LICENSE
|
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
125
|
version: '0'
|
126
126
|
requirements: []
|
127
127
|
rubyforge_project:
|
128
|
-
rubygems_version: 2.
|
128
|
+
rubygems_version: 2.7.3
|
129
129
|
signing_key:
|
130
130
|
specification_version: 4
|
131
131
|
summary: New IO for Ruby
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.4.0
|