nio4r 0.2.2-java → 0.3.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.
- data/.travis.yml +6 -2
- data/CHANGES.md +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +16 -11
- data/Rakefile +2 -2
- data/examples/echo_server.rb +9 -0
- data/ext/libev/Changes +34 -2
- data/ext/libev/ev.c +694 -131
- data/ext/libev/ev.h +89 -79
- data/ext/libev/ev_epoll.c +23 -10
- data/ext/libev/ev_kqueue.c +4 -4
- data/ext/libev/ev_poll.c +4 -4
- data/ext/libev/ev_port.c +9 -3
- data/ext/libev/ev_select.c +10 -6
- data/ext/libev/ev_vars.h +3 -2
- data/ext/libev/ev_wrap.h +6 -4
- data/ext/nio4r/monitor.c +23 -6
- data/ext/nio4r/org/nio4r/Nio4r.java +409 -0
- data/ext/nio4r/selector.c +36 -8
- data/lib/nio.rb +4 -7
- data/lib/nio/monitor.rb +15 -4
- data/lib/nio/selector.rb +23 -6
- data/lib/nio/version.rb +1 -1
- data/lib/nio4r_ext.jar +0 -0
- data/nio4r.gemspec +4 -3
- data/spec/nio/acceptables_spec.rb +30 -0
- data/spec/nio/monitor_spec.rb +37 -22
- data/spec/nio/selectables_spec.rb +178 -0
- data/spec/nio/selector_spec.rb +71 -173
- data/tasks/extension.rake +6 -5
- metadata +33 -29
- data/lib/nio/jruby/monitor.rb +0 -42
- data/lib/nio/jruby/selector.rb +0 -136
data/ext/nio4r/selector.c
CHANGED
@@ -209,9 +209,9 @@ static VALUE NIO_Selector_register_synchronized(VALUE *args)
|
|
209
209
|
rb_raise(rb_eArgError, "this IO is already registered with selector");
|
210
210
|
|
211
211
|
/* Create a new NIO::Monitor */
|
212
|
-
monitor_args[0] =
|
213
|
-
monitor_args[1] =
|
214
|
-
monitor_args[2] =
|
212
|
+
monitor_args[0] = io;
|
213
|
+
monitor_args[1] = interests;
|
214
|
+
monitor_args[2] = self;
|
215
215
|
|
216
216
|
monitor = rb_class_new_instance(3, monitor_args, cNIO_Monitor);
|
217
217
|
rb_hash_aset(selectables, io, monitor);
|
@@ -239,7 +239,7 @@ static VALUE NIO_Selector_deregister_synchronized(VALUE *args)
|
|
239
239
|
monitor = rb_hash_delete(selectables, io);
|
240
240
|
|
241
241
|
if(monitor != Qnil) {
|
242
|
-
rb_funcall(monitor, rb_intern("close"),
|
242
|
+
rb_funcall(monitor, rb_intern("close"), 1, Qfalse);
|
243
243
|
}
|
244
244
|
|
245
245
|
return monitor;
|
@@ -262,6 +262,10 @@ static VALUE NIO_Selector_select(int argc, VALUE *argv, VALUE self)
|
|
262
262
|
|
263
263
|
rb_scan_args(argc, argv, "01", &timeout);
|
264
264
|
|
265
|
+
if(timeout != Qnil && NUM2DBL(timeout) < 0) {
|
266
|
+
rb_raise(rb_eArgError, "time interval must be positive");
|
267
|
+
}
|
268
|
+
|
265
269
|
args[0] = self;
|
266
270
|
args[1] = timeout;
|
267
271
|
|
@@ -280,6 +284,10 @@ static VALUE NIO_Selector_select_each(int argc, VALUE *argv, VALUE self)
|
|
280
284
|
|
281
285
|
rb_scan_args(argc, argv, "01", &timeout);
|
282
286
|
|
287
|
+
if(timeout != Qnil && NUM2DBL(timeout) < 0) {
|
288
|
+
rb_raise(rb_eArgError, "time interval must be positive");
|
289
|
+
}
|
290
|
+
|
283
291
|
args[0] = self;
|
284
292
|
args[1] = timeout;
|
285
293
|
|
@@ -290,12 +298,21 @@ static VALUE NIO_Selector_select_each(int argc, VALUE *argv, VALUE self)
|
|
290
298
|
static VALUE NIO_Selector_select_synchronized(VALUE *args)
|
291
299
|
{
|
292
300
|
struct NIO_Selector *selector;
|
293
|
-
int ready = NIO_Selector_fill_ready_buffer(args);
|
301
|
+
int i, ready = NIO_Selector_fill_ready_buffer(args);
|
294
302
|
|
295
303
|
Data_Get_Struct(args[0], struct NIO_Selector, selector);
|
296
304
|
|
297
305
|
if(ready > 0) {
|
298
|
-
|
306
|
+
if(rb_block_given_p()) {
|
307
|
+
for(i = 0; i < ready; i++) {
|
308
|
+
rb_yield(selector->ready_buffer[i]);
|
309
|
+
}
|
310
|
+
|
311
|
+
return INT2NUM(ready);
|
312
|
+
} else {
|
313
|
+
/* new4 memcpys the ready buffer */
|
314
|
+
return rb_ary_new4(ready, selector->ready_buffer);
|
315
|
+
}
|
299
316
|
} else {
|
300
317
|
return Qnil;
|
301
318
|
}
|
@@ -335,7 +352,8 @@ static int NIO_Selector_fill_ready_buffer(VALUE *args)
|
|
335
352
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_ALONE)
|
336
353
|
/* Implement the optional timeout (if any) as a ev_timer */
|
337
354
|
if(timeout != Qnil) {
|
338
|
-
|
355
|
+
/* It seems libev is not a fan of timers being zero, so fudge a little */
|
356
|
+
selector->timer.repeat = NUM2DBL(timeout) + 0.0001;
|
339
357
|
ev_timer_again(selector->ev_loop, &selector->timer);
|
340
358
|
} else {
|
341
359
|
ev_timer_stop(selector->ev_loop, &selector->timer);
|
@@ -455,7 +473,17 @@ static void NIO_Selector_wakeup_callback(struct ev_loop *ev_loop, struct ev_io *
|
|
455
473
|
|
456
474
|
/* This gets called from individual monitors. We must be careful here because
|
457
475
|
the GIL isn't held, so we must rely only on standard C and can't touch
|
458
|
-
anything Ruby-related
|
476
|
+
anything Ruby-related.
|
477
|
+
|
478
|
+
It's scary because there's a VALUE here, and VALUEs are a Ruby thing,
|
479
|
+
however we're not going to dereference that VALUE or attempt to do anything
|
480
|
+
with it. We just treat it as completely opaque until we have the GIL back.
|
481
|
+
|
482
|
+
In order for this function to even get called, the monitor the VALUE points to
|
483
|
+
must be attached to this Selector, and if it's attached to this Selector
|
484
|
+
then we hold a reference to it in the @selectables instance variable, so
|
485
|
+
there's no danger of this monitor getting garbage collected before we have
|
486
|
+
the GIL back as we hold a reference. */
|
459
487
|
void NIO_Selector_handle_event(struct NIO_Selector *selector, VALUE monitor, int revents)
|
460
488
|
{
|
461
489
|
/* Grow the ready buffer if it's too small */
|
data/lib/nio.rb
CHANGED
@@ -15,16 +15,13 @@ if ENV["NIO4R_PURE"]
|
|
15
15
|
require 'nio/selector'
|
16
16
|
NIO::ENGINE = 'select'
|
17
17
|
else
|
18
|
+
require 'nio4r_ext'
|
19
|
+
|
18
20
|
if defined?(JRUBY_VERSION)
|
19
21
|
require 'java'
|
20
|
-
|
21
|
-
require 'nio/jruby/selector'
|
22
|
+
org.nio4r.Nio4r.new.load(JRuby.runtime, false)
|
22
23
|
NIO::ENGINE = 'java'
|
23
24
|
else
|
24
|
-
require 'nio4r_ext'
|
25
25
|
NIO::ENGINE = 'libev'
|
26
26
|
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# TIMTOWTDI!!!
|
30
|
-
Nio = NIO
|
27
|
+
end
|
data/lib/nio/monitor.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
module NIO
|
2
2
|
# Monitors watch IO objects for specific events
|
3
3
|
class Monitor
|
4
|
-
attr_reader :io, :interests
|
4
|
+
attr_reader :io, :interests, :selector
|
5
5
|
attr_accessor :value, :readiness
|
6
6
|
|
7
7
|
# :nodoc
|
8
|
-
def initialize(io, interests)
|
9
|
-
|
8
|
+
def initialize(io, interests, selector)
|
9
|
+
unless io.is_a?(IO)
|
10
|
+
if IO.respond_to? :try_convert
|
11
|
+
io = IO.try_convert(io)
|
12
|
+
elsif io.respond_to? :to_io
|
13
|
+
io = io.to_io
|
14
|
+
end
|
15
|
+
|
16
|
+
raise TypeError, "can't convert #{io.class} into IO" unless io.is_a? IO
|
17
|
+
end
|
18
|
+
|
19
|
+
@io, @interests, @selector = io, interests, selector
|
10
20
|
@closed = false
|
11
21
|
end
|
12
22
|
|
@@ -25,8 +35,9 @@ module NIO
|
|
25
35
|
def closed?; @closed; end
|
26
36
|
|
27
37
|
# Deactivate this monitor
|
28
|
-
def close
|
38
|
+
def close(deregister = true)
|
29
39
|
@closed = true
|
40
|
+
@selector.deregister(io) if deregister
|
30
41
|
end
|
31
42
|
end
|
32
43
|
end
|
data/lib/nio/selector.rb
CHANGED
@@ -20,7 +20,7 @@ module NIO
|
|
20
20
|
@lock.synchronize do
|
21
21
|
raise ArgumentError, "this IO is already registered with the selector" if @selectables[io]
|
22
22
|
|
23
|
-
monitor = Monitor.new(io, interest)
|
23
|
+
monitor = Monitor.new(io, interest, self)
|
24
24
|
@selectables[io] = monitor
|
25
25
|
|
26
26
|
monitor
|
@@ -31,7 +31,7 @@ module NIO
|
|
31
31
|
def deregister(io)
|
32
32
|
@lock.synchronize do
|
33
33
|
monitor = @selectables.delete io
|
34
|
-
monitor.close if monitor
|
34
|
+
monitor.close(false) if monitor and not monitor.closed?
|
35
35
|
monitor
|
36
36
|
end
|
37
37
|
end
|
@@ -54,7 +54,12 @@ module NIO
|
|
54
54
|
ready_readers, ready_writers = Kernel.select readers, writers, [], timeout
|
55
55
|
return unless ready_readers # timeout or wakeup
|
56
56
|
|
57
|
-
|
57
|
+
if block_given?
|
58
|
+
result = 0
|
59
|
+
else
|
60
|
+
result = []
|
61
|
+
end
|
62
|
+
|
58
63
|
ready_readers.each do |io|
|
59
64
|
if io == @wakeup
|
60
65
|
# Clear all wakeup signals we've received by reading them
|
@@ -71,7 +76,13 @@ module NIO
|
|
71
76
|
else
|
72
77
|
monitor = @selectables[io]
|
73
78
|
monitor.readiness = :r
|
74
|
-
|
79
|
+
|
80
|
+
if block_given?
|
81
|
+
yield monitor
|
82
|
+
result += 1
|
83
|
+
else
|
84
|
+
result << monitor
|
85
|
+
end
|
75
86
|
end
|
76
87
|
end
|
77
88
|
|
@@ -82,11 +93,17 @@ module NIO
|
|
82
93
|
ios.each do |io|
|
83
94
|
monitor = @selectables[io]
|
84
95
|
monitor.readiness = readiness
|
85
|
-
|
96
|
+
|
97
|
+
if block_given?
|
98
|
+
yield monitor
|
99
|
+
result += 1
|
100
|
+
else
|
101
|
+
result << monitor
|
102
|
+
end
|
86
103
|
end
|
87
104
|
end
|
88
105
|
|
89
|
-
|
106
|
+
result
|
90
107
|
end
|
91
108
|
end
|
92
109
|
|
data/lib/nio/version.rb
CHANGED
data/lib/nio4r_ext.jar
ADDED
Binary file
|
data/nio4r.gemspec
CHANGED
@@ -15,8 +15,9 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = NIO::VERSION
|
17
17
|
# gem.extensions = ["ext/nio4r/extconf.rb"]
|
18
|
-
gem.platform
|
19
|
-
|
18
|
+
gem.platform = 'java'
|
19
|
+
|
20
|
+
gem.add_development_dependency "rake-compiler"
|
20
21
|
gem.add_development_dependency "rake"
|
21
|
-
gem.add_development_dependency "rspec"
|
22
|
+
gem.add_development_dependency "rspec"
|
22
23
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
describe "NIO acceptables" do
|
2
|
+
shared_context "an NIO acceptable" do
|
3
|
+
let(:selector) { NIO::Selector.new }
|
4
|
+
|
5
|
+
it "selects for read readiness" do
|
6
|
+
waiting_monitor = selector.register(unacceptable_subject, :r)
|
7
|
+
ready_monitor = selector.register(acceptable_subject, :r)
|
8
|
+
|
9
|
+
ready_monitors = selector.select
|
10
|
+
ready_monitors.should include ready_monitor
|
11
|
+
ready_monitors.should_not include waiting_monitor
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe TCPServer do
|
16
|
+
let(:tcp_port) { 23456 }
|
17
|
+
|
18
|
+
let :acceptable_subject do
|
19
|
+
server = TCPServer.new("localhost", tcp_port)
|
20
|
+
TCPSocket.open("localhost", tcp_port)
|
21
|
+
server
|
22
|
+
end
|
23
|
+
|
24
|
+
let :unacceptable_subject do
|
25
|
+
TCPServer.new("localhost", tcp_port + 1)
|
26
|
+
end
|
27
|
+
|
28
|
+
it_behaves_like "an NIO acceptable"
|
29
|
+
end
|
30
|
+
end
|
data/spec/nio/monitor_spec.rb
CHANGED
@@ -1,21 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe NIO::Monitor do
|
4
|
-
let
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
let(:pipes) { IO.pipe }
|
5
|
+
let(:reader) { pipes.first }
|
6
|
+
let(:writer) { pipes.last }
|
7
|
+
let(:selector) { NIO::Selector.new }
|
8
|
+
|
9
|
+
subject { selector.register(reader, :rw) }
|
10
|
+
let(:peer) { selector.register(writer, :rw) }
|
11
|
+
after { selector.close }
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
it "knows its interests" do
|
14
|
+
subject.interests.should == :rw
|
15
|
+
peer.interests.should == :rw
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
18
|
+
it "knows its IO object" do
|
19
|
+
subject.io.should == reader
|
20
|
+
end
|
16
21
|
|
17
|
-
it "knows its
|
18
|
-
subject.
|
22
|
+
it "knows its selector" do
|
23
|
+
subject.selector.should == selector
|
19
24
|
end
|
20
25
|
|
21
26
|
it "stores arbitrary values" do
|
@@ -23,24 +28,34 @@ describe NIO::Monitor do
|
|
23
28
|
subject.value.should == 42
|
24
29
|
end
|
25
30
|
|
26
|
-
it "knows what IO objects are ready for" do
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
it "knows what operations IO objects are ready for" do
|
32
|
+
# For whatever odd reason this breaks unless we eagerly evaluate subject
|
33
|
+
reader_monitor, writer_monitor = subject, peer
|
34
|
+
|
35
|
+
selected = selector.select(0)
|
36
|
+
selected.should_not include(reader_monitor)
|
37
|
+
selected.should include(writer_monitor)
|
38
|
+
|
39
|
+
writer_monitor.readiness.should == :w
|
40
|
+
writer_monitor.should_not be_readable
|
41
|
+
writer_monitor.should be_writable
|
42
|
+
|
31
43
|
writer << "loldata"
|
32
|
-
selector = NIO::Selector.new
|
33
|
-
subject = selector.register(reader, :r)
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
45
|
+
selected = selector.select(0)
|
46
|
+
selected.should include(reader_monitor)
|
47
|
+
|
48
|
+
reader_monitor.readiness.should == :r
|
49
|
+
reader_monitor.should be_readable
|
50
|
+
reader_monitor.should_not be_writable
|
39
51
|
end
|
40
52
|
|
41
53
|
it "closes" do
|
42
54
|
subject.should_not be_closed
|
55
|
+
selector.registered?(reader).should be_true
|
56
|
+
|
43
57
|
subject.close
|
44
58
|
subject.should be_closed
|
59
|
+
selector.registered?(reader).should be_false
|
45
60
|
end
|
46
61
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
describe "NIO selectables" do
|
2
|
+
let(:selector) { NIO::Selector.new }
|
3
|
+
|
4
|
+
shared_context "an NIO selectable" do
|
5
|
+
it "selects readable objects" do
|
6
|
+
monitor = selector.register(readable_subject, :r)
|
7
|
+
selector.select(0).should include monitor
|
8
|
+
end
|
9
|
+
|
10
|
+
it "does not select unreadable objects" do
|
11
|
+
monitor = selector.register(unreadable_subject, :r)
|
12
|
+
selector.select(0).should be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "selects writable objects" do
|
16
|
+
monitor = selector.register(writable_subject, :w)
|
17
|
+
selector.select(0).should include monitor
|
18
|
+
end
|
19
|
+
|
20
|
+
it "does not select unwritable objects" do
|
21
|
+
monitor = selector.register(unwritable_subject, :w)
|
22
|
+
selector.select(0).should be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
shared_context "an NIO selectable stream" do
|
27
|
+
let(:stream) { pair.first }
|
28
|
+
let(:peer) { pair.last }
|
29
|
+
|
30
|
+
it "selects readable when the other end closes" do
|
31
|
+
monitor = selector.register(stream, :r)
|
32
|
+
selector.select(0).should be_nil
|
33
|
+
|
34
|
+
peer.close
|
35
|
+
selector.select(0).should include monitor
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "IO.pipe" do
|
40
|
+
let(:pair) { IO.pipe }
|
41
|
+
|
42
|
+
let :unreadable_subject do pair.first end
|
43
|
+
let :readable_subject do
|
44
|
+
pipe, peer = pair
|
45
|
+
peer << "data"
|
46
|
+
pipe
|
47
|
+
end
|
48
|
+
|
49
|
+
let :writable_subject do pair.last end
|
50
|
+
let :unwritable_subject do
|
51
|
+
reader, pipe = IO.pipe
|
52
|
+
|
53
|
+
begin
|
54
|
+
pipe.write_nonblock "JUNK IN THE TUBES"
|
55
|
+
_, writers = select [], [pipe], [], 0
|
56
|
+
rescue Errno::EPIPE
|
57
|
+
break
|
58
|
+
end while writers and writers.include? pipe
|
59
|
+
|
60
|
+
pipe
|
61
|
+
end
|
62
|
+
|
63
|
+
it_behaves_like "an NIO selectable"
|
64
|
+
it_behaves_like "an NIO selectable stream"
|
65
|
+
end
|
66
|
+
|
67
|
+
describe TCPSocket do
|
68
|
+
let(:tcp_port) { 12345 }
|
69
|
+
|
70
|
+
let :readable_subject do
|
71
|
+
server = TCPServer.new("localhost", tcp_port)
|
72
|
+
sock = TCPSocket.open("localhost", tcp_port)
|
73
|
+
peer = server.accept
|
74
|
+
peer << "data"
|
75
|
+
sock
|
76
|
+
end
|
77
|
+
|
78
|
+
let :unreadable_subject do
|
79
|
+
TCPServer.new("localhost", tcp_port + 1)
|
80
|
+
sock = TCPSocket.new("localhost", tcp_port + 1)
|
81
|
+
|
82
|
+
# Sanity check to make sure we actually produced an unreadable socket
|
83
|
+
if select([sock], [], [], 0)
|
84
|
+
pending "Failed to produce an unreadable socket"
|
85
|
+
end
|
86
|
+
|
87
|
+
sock
|
88
|
+
end
|
89
|
+
|
90
|
+
let :writable_subject do
|
91
|
+
TCPServer.new("localhost", tcp_port + 2)
|
92
|
+
TCPSocket.new("localhost", tcp_port + 2)
|
93
|
+
end
|
94
|
+
|
95
|
+
let :unwritable_subject do
|
96
|
+
server = TCPServer.new("localhost", tcp_port + 3)
|
97
|
+
sock = TCPSocket.open("localhost", tcp_port + 3)
|
98
|
+
peer = server.accept
|
99
|
+
|
100
|
+
begin
|
101
|
+
sock.write_nonblock "X" * 1024
|
102
|
+
_, writers = select [], [sock], [], 0
|
103
|
+
end while writers and writers.include? sock
|
104
|
+
|
105
|
+
# I think the kernel might manage to drain its buffer a bit even after
|
106
|
+
# the socket first goes unwritable. Attempt to sleep past this and then
|
107
|
+
# attempt to write again
|
108
|
+
sleep 0.1
|
109
|
+
|
110
|
+
# Once more for good measure!
|
111
|
+
begin
|
112
|
+
sock.write_nonblock "X" * 1024
|
113
|
+
rescue Errno::EWOULDBLOCK
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sanity check to make sure we actually produced an unwritable socket
|
117
|
+
if select([], [sock], [], 0)
|
118
|
+
pending "Failed to produce an unwritable socket"
|
119
|
+
end
|
120
|
+
|
121
|
+
sock
|
122
|
+
end
|
123
|
+
|
124
|
+
let :pair do
|
125
|
+
server = TCPServer.new("localhost", tcp_port + 4)
|
126
|
+
client = TCPSocket.open("localhost", tcp_port + 4)
|
127
|
+
[client, server.accept]
|
128
|
+
end
|
129
|
+
|
130
|
+
it_behaves_like "an NIO selectable"
|
131
|
+
it_behaves_like "an NIO selectable stream"
|
132
|
+
|
133
|
+
it "selects writable when connected" do
|
134
|
+
server = TCPServer.new('127.0.0.1', tcp_port + 5)
|
135
|
+
|
136
|
+
client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
137
|
+
monitor = selector.register(client, :w)
|
138
|
+
|
139
|
+
expect do
|
140
|
+
client.connect_nonblock Socket.sockaddr_in(tcp_port + 5, '127.0.0.1')
|
141
|
+
end.to raise_exception Errno::EINPROGRESS
|
142
|
+
|
143
|
+
selector.select(0).should include monitor
|
144
|
+
result = client.getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR)
|
145
|
+
result.unpack('i').first.should be_zero
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe UDPSocket do
|
150
|
+
let(:udp_port) { 23456 }
|
151
|
+
|
152
|
+
let :readable_subject do
|
153
|
+
sock = UDPSocket.new
|
154
|
+
sock.bind('localhost', udp_port)
|
155
|
+
|
156
|
+
peer = UDPSocket.new
|
157
|
+
peer.send("hi there", 0, 'localhost', udp_port)
|
158
|
+
|
159
|
+
sock
|
160
|
+
end
|
161
|
+
|
162
|
+
let :unreadable_subject do
|
163
|
+
sock = UDPSocket.new
|
164
|
+
sock.bind('localhost', udp_port + 1)
|
165
|
+
sock
|
166
|
+
end
|
167
|
+
|
168
|
+
let :writable_subject do
|
169
|
+
pending "come up with a writable UDPSocket example"
|
170
|
+
end
|
171
|
+
|
172
|
+
let :unwritable_subject do
|
173
|
+
pending "come up with a UDPSocket that's blocked on writing"
|
174
|
+
end
|
175
|
+
|
176
|
+
it_behaves_like "an NIO selectable"
|
177
|
+
end
|
178
|
+
end
|