nio4r 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,11 +6,17 @@ require 'spec_helper'
6
6
  TIMEOUT_PRECISION = 0.1
7
7
 
8
8
  describe NIO::Selector do
9
- it "monitors IO objects" do
10
- pipe, _ = IO.pipe
9
+ context "register" do
10
+ it "registers IO objects" do
11
+ pipe, _ = IO.pipe
12
+
13
+ monitor = subject.register(pipe, :r)
14
+ monitor.should_not be_closed
15
+ end
11
16
 
12
- monitor = subject.register(pipe, :r)
13
- monitor.should_not be_closed
17
+ it "raises TypeError if asked to register non-IO objects" do
18
+ expect { subject.register(42, :r) }.to raise_exception TypeError
19
+ end
14
20
  end
15
21
 
16
22
  it "knows which IO objects are registered" do
@@ -30,22 +36,31 @@ describe NIO::Selector do
30
36
  monitor.should be_closed
31
37
  end
32
38
 
33
- it "waits for a timeout when selecting" do
34
- reader, writer = IO.pipe
35
- monitor = subject.register(reader, :r)
39
+ context "timeouts" do
40
+ it "waits for a timeout when selecting" do
41
+ reader, writer = IO.pipe
42
+ monitor = subject.register(reader, :r)
36
43
 
37
- payload = "hi there"
38
- writer << payload
44
+ payload = "hi there"
45
+ writer << payload
39
46
 
40
- timeout = 0.5
41
- started_at = Time.now
42
- subject.select(timeout).should include monitor
43
- (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(0)
44
- reader.read_nonblock(payload.size)
47
+ timeout = 0.5
48
+ started_at = Time.now
49
+ subject.select(timeout).should include monitor
50
+ (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(0)
51
+ reader.read_nonblock(payload.size)
45
52
 
46
- started_at = Time.now
47
- subject.select(timeout).should be_nil
48
- (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(timeout)
53
+ started_at = Time.now
54
+ subject.select(timeout).should be_nil
55
+ (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(timeout)
56
+ end
57
+
58
+ it "raises ArgumentError if given a negative timeout" do
59
+ reader, _ = IO.pipe
60
+ subject.register(reader, :r)
61
+
62
+ expect { subject.select(-1) }.to raise_exception(ArgumentError)
63
+ end
49
64
  end
50
65
 
51
66
  context "wakeup" do
@@ -74,6 +89,45 @@ describe NIO::Selector do
74
89
  end
75
90
  end
76
91
 
92
+ context "select" do
93
+ it "selects IO objects" do
94
+ readable, writer = IO.pipe
95
+ writer << "ohai"
96
+
97
+ unreadable, _ = IO.pipe
98
+
99
+ readable_monitor = subject.register(readable, :r)
100
+ unreadable_monitor = subject.register(unreadable, :r)
101
+
102
+ selected = subject.select(0)
103
+ selected.size.should == 1
104
+ selected.should include(readable_monitor)
105
+ selected.should_not include(unreadable_monitor)
106
+ end
107
+
108
+ it "iterates across selected objects with a block" do
109
+ readable1, writer = IO.pipe
110
+ writer << "ohai"
111
+
112
+ readable2, writer = IO.pipe
113
+ writer << "ohai"
114
+
115
+ unreadable, _ = IO.pipe
116
+
117
+ monitor1 = subject.register(readable1, :r)
118
+ monitor2 = subject.register(readable2, :r)
119
+ monitor3 = subject.register(unreadable, :r)
120
+
121
+ readables = []
122
+ result = subject.select { |monitor| readables << monitor }
123
+ result.should == 2
124
+
125
+ readables.should include(monitor1)
126
+ readables.should include(monitor2)
127
+ readables.should_not include(monitor3)
128
+ end
129
+ end
130
+
77
131
  context "select_each" do
78
132
  it "iterates across ready selectables" do
79
133
  readable1, writer = IO.pipe
@@ -117,160 +171,4 @@ describe NIO::Selector do
117
171
  subject.close
118
172
  subject.should be_closed
119
173
  end
120
-
121
- context "selectables" do
122
- shared_context "an NIO selectable" do
123
- it "selects for read readiness" do
124
- waiting_monitor = subject.register(unreadable_subject, :r)
125
- ready_monitor = subject.register(readable_subject, :r)
126
-
127
- ready_monitors = subject.select
128
- ready_monitors.should include ready_monitor
129
- ready_monitors.should_not include waiting_monitor
130
- end
131
-
132
- it "selects for write readiness" do
133
- waiting_monitor = subject.register(unwritable_subject, :w)
134
- ready_monitor = subject.register(writable_subject, :w)
135
-
136
- ready_monitors = subject.select(0.1)
137
-
138
- ready_monitors.should include ready_monitor
139
- ready_monitors.should_not include waiting_monitor
140
- end
141
- end
142
-
143
- context "IO.pipe" do
144
- let :readable_subject do
145
- pipe, peer = IO.pipe
146
- peer << "data"
147
- pipe
148
- end
149
-
150
- let :unreadable_subject do
151
- pipe, _ = IO.pipe
152
- pipe
153
- end
154
-
155
- let :writable_subject do
156
- _, pipe = IO.pipe
157
- pipe
158
- end
159
-
160
- let :unwritable_subject do
161
- reader, pipe = IO.pipe
162
-
163
- begin
164
- pipe.write_nonblock "JUNK IN THE TUBES"
165
- _, writers = select [], [pipe], [], 0
166
- rescue Errno::EPIPE
167
- break
168
- end while writers and writers.include? pipe
169
-
170
- pipe
171
- end
172
-
173
- it_behaves_like "an NIO selectable"
174
- end
175
-
176
- context TCPSocket do
177
- let(:tcp_port) { 12345 }
178
-
179
- let :readable_subject do
180
- server = TCPServer.new("localhost", tcp_port)
181
- sock = TCPSocket.open("localhost", tcp_port)
182
- peer = server.accept
183
- peer << "data"
184
- sock
185
- end
186
-
187
- let :unreadable_subject do
188
- if defined?(JRUBY_VERSION) and ENV['TRAVIS']
189
- pending "This is sporadically showing up readable on JRuby in CI"
190
- else
191
- TCPServer.new("localhost", tcp_port + 1)
192
- TCPSocket.open("localhost", tcp_port + 1)
193
- end
194
- end
195
-
196
- let :writable_subject do
197
- TCPServer.new("localhost", tcp_port + 2)
198
- TCPSocket.open("localhost", tcp_port + 2)
199
- end
200
-
201
- let :unwritable_subject do
202
- server = TCPServer.new("localhost", tcp_port + 3)
203
- sock = TCPSocket.open("localhost", tcp_port + 3)
204
- peer = server.accept
205
-
206
- begin
207
- sock.write_nonblock "JUNK IN THE TUBES"
208
- _, writers = select [], [sock], [], 0
209
- end while writers and writers.include? sock
210
-
211
- sock
212
- end
213
-
214
- it_behaves_like "an NIO selectable"
215
- end
216
-
217
- context UDPSocket do
218
- let(:udp_port) { 23456 }
219
-
220
- let :readable_subject do
221
- sock = UDPSocket.new
222
- sock.bind('localhost', udp_port)
223
-
224
- peer = UDPSocket.new
225
- peer.send("hi there", 0, 'localhost', udp_port)
226
-
227
- sock
228
- end
229
-
230
- let :unreadable_subject do
231
- sock = UDPSocket.new
232
- sock.bind('localhost', udp_port + 1)
233
- sock
234
- end
235
-
236
- let :writable_subject do
237
- pending "come up with a writable UDPSocket example"
238
- end
239
-
240
- let :unwritable_subject do
241
- pending "come up with a UDPSocket that's blocked on writing"
242
- end
243
-
244
- it_behaves_like "an NIO selectable"
245
- end
246
- end
247
-
248
- context "acceptables" do
249
- shared_context "an NIO acceptable" do
250
- it "selects for read readiness" do
251
- waiting_monitor = subject.register(unacceptable_subject, :r)
252
- ready_monitor = subject.register(acceptable_subject, :r)
253
-
254
- ready_monitors = subject.select
255
- ready_monitors.should include ready_monitor
256
- ready_monitors.should_not include waiting_monitor
257
- end
258
- end
259
-
260
- context TCPServer do
261
- let(:tcp_port) { 23456 }
262
-
263
- let :acceptable_subject do
264
- server = TCPServer.new("localhost", tcp_port)
265
- TCPSocket.open("localhost", tcp_port)
266
- server
267
- end
268
-
269
- let :unacceptable_subject do
270
- TCPServer.new("localhost", tcp_port + 1)
271
- end
272
-
273
- it_behaves_like "an NIO acceptable"
274
- end
275
- end
276
174
  end
data/tasks/extension.rake CHANGED
@@ -1,9 +1,10 @@
1
- require 'rake/extensiontask'
2
-
3
- if defined?(JRUBY_VERSION)
4
- # Don't build the native extension on JRuby since it uses native Java NIO
5
- task :compile
1
+ if defined? JRUBY_VERSION
2
+ require 'rake/javaextensiontask'
3
+ Rake::JavaExtensionTask.new('nio4r_ext') do |ext|
4
+ ext.ext_dir = 'ext/nio4r'
5
+ end
6
6
  else
7
+ require 'rake/extensiontask'
7
8
  Rake::ExtensionTask.new('nio4r_ext') do |ext|
8
9
  ext.ext_dir = 'ext/nio4r'
9
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nio4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-08 00:00:00.000000000 Z
12
+ date: 2012-02-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake-compiler
16
- requirement: &70252075568080 !ruby/object:Gem::Requirement
16
+ requirement: &70285234600680 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ~>
19
+ - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.7.9
21
+ version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70252075568080
24
+ version_requirements: *70285234600680
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70252075567520 !ruby/object:Gem::Requirement
27
+ requirement: &70285234600040 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,18 +32,18 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70252075567520
35
+ version_requirements: *70285234600040
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70252071622900 !ruby/object:Gem::Requirement
38
+ requirement: &70285234599480 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - ~>
41
+ - - ! '>='
42
42
  - !ruby/object:Gem::Version
43
- version: 2.7.0
43
+ version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70252071622900
46
+ version_requirements: *70285234599480
47
47
  description: New IO for Ruby
48
48
  email:
49
49
  - tony.arcieri@gmail.com
@@ -81,15 +81,16 @@ files:
81
81
  - ext/nio4r/monitor.c
82
82
  - ext/nio4r/nio4r.h
83
83
  - ext/nio4r/nio4r_ext.c
84
+ - ext/nio4r/org/nio4r/Nio4r.java
84
85
  - ext/nio4r/selector.c
85
86
  - lib/nio.rb
86
- - lib/nio/jruby/monitor.rb
87
- - lib/nio/jruby/selector.rb
88
87
  - lib/nio/monitor.rb
89
88
  - lib/nio/selector.rb
90
89
  - lib/nio/version.rb
91
90
  - nio4r.gemspec
91
+ - spec/nio/acceptables_spec.rb
92
92
  - spec/nio/monitor_spec.rb
93
+ - spec/nio/selectables_spec.rb
93
94
  - spec/nio/selector_spec.rb
94
95
  - spec/spec_helper.rb
95
96
  - tasks/extension.rake
@@ -119,7 +120,9 @@ signing_key:
119
120
  specification_version: 3
120
121
  summary: NIO provides a high performance selector API for monitoring IO objects
121
122
  test_files:
123
+ - spec/nio/acceptables_spec.rb
122
124
  - spec/nio/monitor_spec.rb
125
+ - spec/nio/selectables_spec.rb
123
126
  - spec/nio/selector_spec.rb
124
127
  - spec/spec_helper.rb
125
128
  has_rdoc:
@@ -1,42 +0,0 @@
1
- module NIO
2
- # Monitors watch Channels for specific events
3
- class Monitor
4
- attr_accessor :value, :io
5
-
6
- # :nodoc
7
- def initialize(io, selection_key)
8
- @io, @key = io, selection_key
9
- selection_key.attach self
10
- @closed = false
11
- end
12
-
13
- # Obtain the interests for this monitor
14
- def interests
15
- Selector.iops2sym @key.interestOps
16
- end
17
-
18
- # What is the IO object ready for?
19
- def readiness
20
- Selector.iops2sym @key.readyOps
21
- end
22
-
23
- # Is the IO object readable?
24
- def readable?
25
- readiness == :r || readiness == :rw
26
- end
27
-
28
- # Is the IO object writable?
29
- def writable?
30
- readiness == :w || readiness == :rw
31
- end
32
- alias_method :writeable?, :writable?
33
-
34
- # Is this monitor closed?
35
- def closed?; @closed; end
36
-
37
- # Deactivate this monitor
38
- def close
39
- @closed = true
40
- end
41
- end
42
- end
@@ -1,136 +0,0 @@
1
- module NIO
2
- # Selectors monitor IO objects for events of interest
3
- class Selector
4
- java_import "java.nio.channels.Selector"
5
- java_import "java.nio.channels.SelectionKey"
6
-
7
- # Convert nio4r interest symbols to Java NIO interest ops
8
- def self.sym2iops(interest, channel)
9
- case interest
10
- when :r
11
- if channel.validOps & SelectionKey::OP_ACCEPT != 0
12
- SelectionKey::OP_ACCEPT
13
- else
14
- SelectionKey::OP_READ
15
- end
16
- when :w
17
- if channel.respond_to? :connected? and not channel.connected?
18
- SelectionKey::OP_CONNECT
19
- else
20
- SelectionKey::OP_WRITE
21
- end
22
- when :rw
23
- super(:r, channel) | super(:w, channel)
24
- else raise ArgumentError, "invalid interest type: #{interest}"
25
- end
26
- end
27
-
28
- # Convert Java NIO interest ops to the corresponding Ruby symbols
29
- def self.iops2sym(interest_ops)
30
- case interest_ops
31
- when SelectionKey::OP_READ, SelectionKey::OP_ACCEPT
32
- :r
33
- when SelectionKey::OP_WRITE, SelectionKey::OP_CONNECT
34
- :w
35
- when SelectionKey::OP_READ | SelectionKey::OP_WRITE
36
- :rw
37
- when 0 then nil
38
- else raise ArgumentError, "unknown interest op combination: 0x#{interest_ops.to_s(16)}"
39
- end
40
- end
41
-
42
- # Create a new NIO::Selector
43
- def initialize
44
- @java_selector = Selector.open
45
- @select_lock = Mutex.new
46
- end
47
-
48
- # Register interest in an IO object with the selector for the given types
49
- # of events. Valid event types for interest are:
50
- # * :r - is the IO readable?
51
- # * :w - is the IO writeable?
52
- # * :rw - is the IO either readable or writeable?
53
- def register(io, interest)
54
- java_channel = io.to_channel
55
- java_channel.configureBlocking(false)
56
- interest_ops = self.class.sym2iops(interest, java_channel)
57
-
58
- begin
59
- selector_key = java_channel.register @java_selector, interest_ops
60
- rescue NativeException => ex
61
- case ex.cause
62
- when java.lang.IllegalArgumentException
63
- raise ArgumentError, "invalid interest type for #{java_channel}: #{interest}"
64
- else raise
65
- end
66
- end
67
-
68
- NIO::Monitor.new(io, selector_key)
69
- end
70
-
71
- # Deregister the given IO object from the selector
72
- def deregister(io)
73
- key = io.to_channel.keyFor(@java_selector)
74
- return unless key
75
-
76
- monitor = key.attachment
77
- monitor.close
78
- monitor
79
- end
80
-
81
- # Is the given IO object registered with the selector?
82
- def registered?(io)
83
- key = io.to_channel.keyFor(@java_selector)
84
- return unless key
85
- !key.attachment.closed?
86
- end
87
-
88
- # Select which monitors are ready
89
- def select(timeout = nil)
90
- selected = []
91
- ready = select_each(timeout) { |monitor| selected << monitor }
92
- return unless ready
93
-
94
- selected
95
- end
96
-
97
- # Iterate across all selectable monitors
98
- def select_each(timeout = nil)
99
- @select_lock.synchronize do
100
- if timeout == 0
101
- # The Java NIO API thinks zero means you want to BLOCK FOREVER o_O
102
- # How about we don't block at all instead?
103
- ready = @java_selector.selectNow
104
- elsif timeout
105
- ready = @java_selector.select(timeout * 1000)
106
- else
107
- ready = @java_selector.select
108
- end
109
-
110
- return unless ready > 0 # timeout or wakeup
111
-
112
- @java_selector.selectedKeys.each { |key| yield key.attachment }
113
- @java_selector.selectedKeys.clear
114
-
115
- ready
116
- end
117
- end
118
-
119
- # Wake up the other thread that's currently blocking on this selector
120
- def wakeup
121
- raise IOError, "selector is closed" if closed?
122
- @java_selector.wakeup
123
- nil
124
- end
125
-
126
- # Close this selector
127
- def close
128
- @java_selector.close
129
- end
130
-
131
- # Is this selector closed?
132
- def closed?
133
- !@java_selector.isOpen
134
- end
135
- end
136
- end