nio4r 0.2.2 → 0.3.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.
@@ -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