nio4r 0.2.0 → 2.7.5

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 (60) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/libev/Changes +232 -3
  4. data/ext/libev/LICENSE +2 -1
  5. data/ext/libev/README +11 -10
  6. data/ext/libev/ev.c +2249 -473
  7. data/ext/libev/ev.h +165 -135
  8. data/ext/libev/ev_epoll.c +68 -36
  9. data/ext/libev/ev_iouring.c +694 -0
  10. data/ext/libev/ev_kqueue.c +44 -18
  11. data/ext/libev/ev_linuxaio.c +620 -0
  12. data/ext/libev/ev_poll.c +28 -20
  13. data/ext/libev/ev_port.c +23 -10
  14. data/ext/libev/ev_select.c +18 -12
  15. data/ext/libev/ev_vars.h +65 -19
  16. data/ext/libev/ev_win32.c +16 -7
  17. data/ext/libev/ev_wrap.h +236 -160
  18. data/ext/nio4r/.clang-format +16 -0
  19. data/ext/nio4r/bytebuffer.c +465 -0
  20. data/ext/nio4r/extconf.rb +44 -35
  21. data/ext/nio4r/libev.h +3 -4
  22. data/ext/nio4r/monitor.c +186 -54
  23. data/ext/nio4r/nio4r.h +17 -23
  24. data/ext/nio4r/nio4r_ext.c +11 -3
  25. data/ext/nio4r/org/nio4r/ByteBuffer.java +295 -0
  26. data/ext/nio4r/org/nio4r/Monitor.java +176 -0
  27. data/ext/nio4r/org/nio4r/Nio4r.java +104 -0
  28. data/ext/nio4r/org/nio4r/Selector.java +297 -0
  29. data/ext/nio4r/selector.c +350 -182
  30. data/lib/nio/bytebuffer.rb +235 -0
  31. data/lib/nio/monitor.rb +100 -8
  32. data/lib/nio/selector.rb +110 -44
  33. data/lib/nio/version.rb +8 -1
  34. data/lib/nio.rb +47 -16
  35. data/lib/nio4r.rb +7 -0
  36. data/license.md +80 -0
  37. data/readme.md +91 -0
  38. data/releases.md +343 -0
  39. data.tar.gz.sig +2 -0
  40. metadata +114 -79
  41. metadata.gz.sig +0 -0
  42. data/.gitignore +0 -20
  43. data/.rspec +0 -4
  44. data/.travis.yml +0 -7
  45. data/CHANGES.md +0 -10
  46. data/Gemfile +0 -4
  47. data/LICENSE.txt +0 -20
  48. data/README.md +0 -171
  49. data/Rakefile +0 -9
  50. data/examples/echo_server.rb +0 -38
  51. data/ext/libev/README.embed +0 -3
  52. data/ext/libev/test_libev_win32.c +0 -123
  53. data/lib/nio/jruby/monitor.rb +0 -42
  54. data/lib/nio/jruby/selector.rb +0 -135
  55. data/nio4r.gemspec +0 -22
  56. data/spec/nio/monitor_spec.rb +0 -46
  57. data/spec/nio/selector_spec.rb +0 -269
  58. data/spec/spec_helper.rb +0 -3
  59. data/tasks/extension.rake +0 -10
  60. data/tasks/rspec.rake +0 -7
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2016, by Upekshe Jayasekera.
5
+ # Copyright, 2016-2017, by Tony Arcieri.
6
+ # Copyright, 2020, by Thomas Dziedzic.
7
+ # Copyright, 2023, by Samuel Williams.
8
+
9
+ module NIO
10
+ # Efficient byte buffers for performant I/O operations
11
+ class ByteBuffer
12
+ include Enumerable
13
+
14
+ attr_reader :position, :limit, :capacity
15
+
16
+ # Insufficient capacity in buffer
17
+ OverflowError = Class.new(IOError)
18
+
19
+ # Not enough data remaining in buffer
20
+ UnderflowError = Class.new(IOError)
21
+
22
+ # Mark has not been set
23
+ MarkUnsetError = Class.new(IOError)
24
+
25
+ # Create a new ByteBuffer, either with a specified capacity or populating
26
+ # it from a given string
27
+ #
28
+ # @param capacity [Integer] size of buffer in bytes
29
+ #
30
+ # @return [NIO::ByteBuffer]
31
+ def initialize(capacity)
32
+ raise TypeError, "no implicit conversion of #{capacity.class} to Integer" unless capacity.is_a?(Integer)
33
+
34
+ @capacity = capacity
35
+ clear
36
+ end
37
+
38
+ # Clear the buffer, resetting it to the default state
39
+ def clear
40
+ @buffer = ("\0" * @capacity).force_encoding(Encoding::BINARY)
41
+ @position = 0
42
+ @limit = @capacity
43
+ @mark = nil
44
+
45
+ self
46
+ end
47
+
48
+ # Set the position to the given value. New position must be less than limit.
49
+ # Preserves mark if it's less than the new position, otherwise clears it.
50
+ #
51
+ # @param new_position [Integer] position in the buffer
52
+ #
53
+ # @raise [ArgumentError] new position was invalid
54
+ def position=(new_position)
55
+ raise ArgumentError, "negative position given" if new_position < 0
56
+ raise ArgumentError, "specified position exceeds capacity" if new_position > @capacity
57
+
58
+ @mark = nil if @mark && @mark > new_position
59
+ @position = new_position
60
+ end
61
+
62
+ # Set the limit to the given value. New limit must be less than capacity.
63
+ # Preserves limit and mark if they're less than the new limit, otherwise
64
+ # sets position to the new limit and clears the mark.
65
+ #
66
+ # @param new_limit [Integer] position in the buffer
67
+ #
68
+ # @raise [ArgumentError] new limit was invalid
69
+ def limit=(new_limit)
70
+ raise ArgumentError, "negative limit given" if new_limit < 0
71
+ raise ArgumentError, "specified limit exceeds capacity" if new_limit > @capacity
72
+
73
+ @position = new_limit if @position > new_limit
74
+ @mark = nil if @mark && @mark > new_limit
75
+ @limit = new_limit
76
+ end
77
+
78
+ # Number of bytes remaining in the buffer before the limit
79
+ #
80
+ # @return [Integer] number of bytes remaining
81
+ def remaining
82
+ @limit - @position
83
+ end
84
+
85
+ # Does the ByteBuffer have any space remaining?
86
+ #
87
+ # @return [true, false]
88
+ def full?
89
+ remaining.zero?
90
+ end
91
+
92
+ # Obtain the requested number of bytes from the buffer, advancing the position.
93
+ # If no length is given, all remaining bytes are consumed.
94
+ #
95
+ # @raise [NIO::ByteBuffer::UnderflowError] not enough data remaining in buffer
96
+ #
97
+ # @return [String] bytes read from buffer
98
+ def get(length = remaining)
99
+ raise ArgumentError, "negative length given" if length < 0
100
+ raise UnderflowError, "not enough data in buffer" if length > @limit - @position
101
+
102
+ result = @buffer[@position...length]
103
+ @position += length
104
+ result
105
+ end
106
+
107
+ # Obtain the byte at a given index in the buffer as an Integer
108
+ #
109
+ # @raise [ArgumentError] index is invalid (either negative or larger than limit)
110
+ #
111
+ # @return [Integer] byte at the given index
112
+ def [](index)
113
+ raise ArgumentError, "negative index given" if index < 0
114
+ raise ArgumentError, "specified index exceeds limit" if index >= @limit
115
+
116
+ @buffer.bytes[index]
117
+ end
118
+
119
+ # Add a String to the buffer
120
+ #
121
+ # @param str [#to_str] data to add to the buffer
122
+ #
123
+ # @raise [TypeError] given a non-string type
124
+ # @raise [NIO::ByteBuffer::OverflowError] buffer is full
125
+ #
126
+ # @return [self]
127
+ def put(str)
128
+ raise TypeError, "expected String, got #{str.class}" unless str.respond_to?(:to_str)
129
+
130
+ str = str.to_str
131
+
132
+ raise OverflowError, "buffer is full" if str.length > @limit - @position
133
+
134
+ @buffer[@position...str.length] = str
135
+ @position += str.length
136
+ self
137
+ end
138
+ alias << put
139
+
140
+ # Perform a non-blocking read from the given IO object into the buffer
141
+ # Reads as much data as is immediately available and returns
142
+ #
143
+ # @param [IO] Ruby IO object to read from
144
+ #
145
+ # @return [Integer] number of bytes read (0 if none were available)
146
+ def read_from(io)
147
+ nbytes = @limit - @position
148
+ raise OverflowError, "buffer is full" if nbytes.zero?
149
+
150
+ bytes_read = IO.try_convert(io).read_nonblock(nbytes, exception: false)
151
+ return 0 if bytes_read == :wait_readable
152
+
153
+ self << bytes_read
154
+ bytes_read.length
155
+ end
156
+
157
+ # Perform a non-blocking write of the buffer's contents to the given I/O object
158
+ # Writes as much data as is immediately possible and returns
159
+ #
160
+ # @param [IO] Ruby IO object to write to
161
+ #
162
+ # @return [Integer] number of bytes written (0 if the write would block)
163
+ def write_to(io)
164
+ nbytes = @limit - @position
165
+ raise UnderflowError, "no data remaining in buffer" if nbytes.zero?
166
+
167
+ bytes_written = IO.try_convert(io).write_nonblock(@buffer[@position...@limit], exception: false)
168
+ return 0 if bytes_written == :wait_writable
169
+
170
+ @position += bytes_written
171
+ bytes_written
172
+ end
173
+
174
+ # Set the buffer's current position as the limit and set the position to 0
175
+ def flip
176
+ @limit = @position
177
+ @position = 0
178
+ @mark = nil
179
+ self
180
+ end
181
+
182
+ # Set the buffer's current position to 0, leaving the limit unchanged
183
+ def rewind
184
+ @position = 0
185
+ @mark = nil
186
+ self
187
+ end
188
+
189
+ # Mark a position to return to using the `#reset` method
190
+ def mark
191
+ @mark = @position
192
+ self
193
+ end
194
+
195
+ # Reset position to the previously marked location
196
+ #
197
+ # @raise [NIO::ByteBuffer::MarkUnsetError] mark has not been set (call `#mark` first)
198
+ def reset
199
+ raise MarkUnsetError, "mark has not been set" unless @mark
200
+
201
+ @position = @mark
202
+ self
203
+ end
204
+
205
+ # Move data between the position and limit to the beginning of the buffer
206
+ # Sets the position to the end of the moved data, and the limit to the capacity
207
+ def compact
208
+ @buffer[0...(@limit - @position)] = @buffer[@position...@limit]
209
+ @position = @limit - @position
210
+ @limit = capacity
211
+ self
212
+ end
213
+
214
+ # Iterate over the bytes in the buffer (as Integers)
215
+ #
216
+ # @return [self]
217
+ def each(&block)
218
+ @buffer[0...@limit].each_byte(&block)
219
+ end
220
+
221
+ # Inspect the state of the buffer
222
+ #
223
+ # @return [String] string describing the state of the buffer
224
+ def inspect
225
+ format(
226
+ "#<%s:0x%x @position=%d @limit=%d @capacity=%d>",
227
+ self.class,
228
+ object_id << 1,
229
+ @position,
230
+ @limit,
231
+ @capacity
232
+ )
233
+ end
234
+ end
235
+ end
data/lib/nio/monitor.rb CHANGED
@@ -1,13 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2011-2018, by Tony Arcieri.
5
+ # Copyright, 2015, by Upekshe Jayasekera.
6
+ # Copyright, 2015, by Vladimir Kochnev.
7
+ # Copyright, 2018-2023, by Samuel Williams.
8
+ # Copyright, 2019-2020, by Gregory Longtin.
9
+
1
10
  module NIO
2
11
  # Monitors watch IO objects for specific events
3
12
  class Monitor
4
- attr_reader :io, :interests
13
+ attr_reader :io, :interests, :selector
5
14
  attr_accessor :value, :readiness
6
15
 
7
- # :nodoc
8
- def initialize(io, interests)
9
- @io, @interests = io, interests
10
- @closed = false
16
+ # :nodoc:
17
+ def initialize(io, interests, selector)
18
+ unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
19
+ unless io.is_a?(IO)
20
+ if IO.respond_to? :try_convert
21
+ io = IO.try_convert(io)
22
+ elsif io.respond_to? :to_io
23
+ io = io.to_io
24
+ end
25
+
26
+ raise TypeError, "can't convert #{io.class} into IO" unless io.is_a? IO
27
+ end
28
+ end
29
+
30
+ @io = io
31
+ @interests = interests
32
+ @selector = selector
33
+ @closed = false
34
+ end
35
+
36
+ # Replace the existing interest set with a new one
37
+ #
38
+ # @param interests [:r, :w, :rw, nil] I/O readiness we're interested in (read/write/readwrite)
39
+ #
40
+ # @return [Symbol] new interests
41
+ def interests=(interests)
42
+ raise EOFError, "monitor is closed" if closed?
43
+ raise ArgumentError, "bad interests: #{interests}" unless [:r, :w, :rw, nil].include?(interests)
44
+
45
+ @interests = interests
46
+ end
47
+
48
+ # Add new interests to the existing interest set
49
+ #
50
+ # @param interests [:r, :w, :rw] new I/O interests (read/write/readwrite)
51
+ #
52
+ # @return [self]
53
+ def add_interest(interest)
54
+ case interest
55
+ when :r
56
+ case @interests
57
+ when :r then @interests = :r
58
+ when :w then @interests = :rw
59
+ when :rw then @interests = :rw
60
+ when nil then @interests = :r
61
+ end
62
+ when :w
63
+ case @interests
64
+ when :r then @interests = :rw
65
+ when :w then @interests = :w
66
+ when :rw then @interests = :rw
67
+ when nil then @interests = :w
68
+ end
69
+ when :rw
70
+ @interests = :rw
71
+ else raise ArgumentError, "bad interests: #{interest}"
72
+ end
73
+ end
74
+
75
+ # Remove interests from the existing interest set
76
+ #
77
+ # @param interests [:r, :w, :rw] I/O interests to remove (read/write/readwrite)
78
+ #
79
+ # @return [self]
80
+ def remove_interest(interest)
81
+ case interest
82
+ when :r
83
+ case @interests
84
+ when :r then @interests = nil
85
+ when :w then @interests = :w
86
+ when :rw then @interests = :w
87
+ when nil then @interests = nil
88
+ end
89
+ when :w
90
+ case @interests
91
+ when :r then @interests = :r
92
+ when :w then @interests = nil
93
+ when :rw then @interests = :r
94
+ when nil then @interests = nil
95
+ end
96
+ when :rw
97
+ @interests = nil
98
+ else raise ArgumentError, "bad interests: #{interest}"
99
+ end
11
100
  end
12
101
 
13
102
  # Is the IO object readable?
@@ -19,14 +108,17 @@ module NIO
19
108
  def writable?
20
109
  readiness == :w || readiness == :rw
21
110
  end
22
- alias_method :writeable?, :writable?
111
+ alias writeable? writable?
23
112
 
24
113
  # Is this monitor closed?
25
- def closed?; @closed; end
114
+ def closed?
115
+ @closed
116
+ end
26
117
 
27
118
  # Deactivate this monitor
28
- def close
119
+ def close(deregister = true)
29
120
  @closed = true
121
+ @selector.deregister(io) if deregister
30
122
  end
31
123
  end
32
124
  end
data/lib/nio/selector.rb CHANGED
@@ -1,8 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2011-2017, by Tony Arcieri.
5
+ # Copyright, 2012, by Logan Bowers.
6
+ # Copyright, 2013, by Sadayuki Furuhashi.
7
+ # Copyright, 2013, by Stephen von Takach.
8
+ # Copyright, 2013, by Tim Carey-Smith.
9
+ # Copyright, 2013, by Ravil Bayramgalin.
10
+ # Copyright, 2014, by Sergey Avseyev.
11
+ # Copyright, 2014, by John Thornton.
12
+ # Copyright, 2015, by Vladimir Kochnev.
13
+ # Copyright, 2015, by Upekshe Jayasekera.
14
+ # Copyright, 2019-2020, by Gregory Longtin.
15
+ # Copyright, 2020-2021, by Joao Fernandes.
16
+ # Copyright, 2023, by Samuel Williams.
17
+
18
+ require "set"
19
+
1
20
  module NIO
2
21
  # Selectors monitor IO objects for events of interest
3
22
  class Selector
23
+ # Return supported backends as symbols
24
+ #
25
+ # See `#backend` method definition for all possible backends
26
+ def self.backends
27
+ [:ruby]
28
+ end
29
+
4
30
  # Create a new NIO::Selector
5
- def initialize
31
+ def initialize(backend = :ruby)
32
+ raise ArgumentError, "unsupported backend: #{backend}" unless [:ruby, nil].include?(backend)
33
+
6
34
  @selectables = {}
7
35
  @lock = Mutex.new
8
36
 
@@ -11,17 +39,40 @@ module NIO
11
39
  @closed = false
12
40
  end
13
41
 
42
+ # Return a symbol representing the backend I/O multiplexing mechanism used.
43
+ # Supported backends are:
44
+ # * :ruby - pure Ruby (i.e IO.select)
45
+ # * :java - Java NIO on JRuby
46
+ # * :epoll - libev w\ Linux epoll
47
+ # * :poll - libev w\ POSIX poll
48
+ # * :kqueue - libev w\ BSD kqueue
49
+ # * :select - libev w\ SysV select
50
+ # * :port - libev w\ I/O completion ports
51
+ # * :linuxaio - libev w\ Linux AIO io_submit (experimental)
52
+ # * :io_uring - libev w\ Linux io_uring (experimental)
53
+ # * :unknown - libev w\ unknown backend
54
+ def backend
55
+ :ruby
56
+ end
57
+
14
58
  # Register interest in an IO object with the selector for the given types
15
59
  # of events. Valid event types for interest are:
16
60
  # * :r - is the IO readable?
17
61
  # * :w - is the IO writeable?
18
62
  # * :rw - is the IO either readable or writeable?
19
63
  def register(io, interest)
64
+ unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
65
+ io = IO.try_convert(io)
66
+ end
67
+
20
68
  @lock.synchronize do
21
- raise ArgumentError, "this IO is already registered with the selector" if @selectables[io]
69
+ raise IOError, "selector is closed" if closed?
70
+
71
+ monitor = @selectables[io]
72
+ raise ArgumentError, "already registered as #{monitor.interests.inspect}" if monitor
22
73
 
23
- monitor = Monitor.new(io, interest)
24
- @selectables[io] = monitor
74
+ monitor = Monitor.new(io, interest, self)
75
+ @selectables[monitor.io] = monitor
25
76
 
26
77
  monitor
27
78
  end
@@ -30,78 +81,79 @@ module NIO
30
81
  # Deregister the given IO object from the selector
31
82
  def deregister(io)
32
83
  @lock.synchronize do
33
- monitor = @selectables.delete io
34
- monitor.close if monitor
84
+ monitor = @selectables.delete IO.try_convert(io)
85
+ monitor.close(false) if monitor && !monitor.closed?
35
86
  monitor
36
87
  end
37
88
  end
38
89
 
39
90
  # Is the given IO object registered with the selector?
40
91
  def registered?(io)
41
- @lock.synchronize { @selectables.has_key? io }
92
+ @lock.synchronize { @selectables.key? io }
42
93
  end
43
94
 
44
95
  # Select which monitors are ready
45
96
  def select(timeout = nil)
97
+ selected_monitors = Set.new
98
+
46
99
  @lock.synchronize do
47
- readers, writers = [@wakeup], []
100
+ readers = [@wakeup]
101
+ writers = []
48
102
 
49
103
  @selectables.each do |io, monitor|
50
104
  readers << io if monitor.interests == :r || monitor.interests == :rw
51
105
  writers << io if monitor.interests == :w || monitor.interests == :rw
106
+ monitor.readiness = nil
52
107
  end
53
108
 
54
- ready_readers, ready_writers = Kernel.select readers, writers, [], timeout
55
- return unless ready_readers # timeout or wakeup
109
+ ready_readers, ready_writers = Kernel.select(readers, writers, [], timeout)
110
+ return unless ready_readers # timeout
56
111
 
57
- results = []
58
112
  ready_readers.each do |io|
59
113
  if io == @wakeup
60
114
  # Clear all wakeup signals we've received by reading them
61
115
  # Wakeups should have level triggered behavior
62
- begin
63
- @wakeup.read_nonblock(1024)
64
-
65
- # Loop until we've drained all incoming events
66
- redo
67
- rescue Errno::EWOULDBLOCK
68
- end
69
-
70
- return
116
+ @wakeup.read(@wakeup.stat.size)
71
117
  else
72
118
  monitor = @selectables[io]
73
119
  monitor.readiness = :r
74
- results << monitor
120
+ selected_monitors << monitor
75
121
  end
76
122
  end
77
123
 
78
- ready_readwriters = ready_readers & ready_writers
79
- ready_writers = ready_writers - ready_readwriters
80
-
81
- [[ready_writers, :w], [ready_readwriters, :rw]].each do |ios, readiness|
82
- ios.each do |io|
83
- monitor = @selectables[io]
84
- monitor.readiness = readiness
85
- results << monitor
86
- end
124
+ ready_writers.each do |io|
125
+ monitor = @selectables[io]
126
+ monitor.readiness = monitor.readiness == :r ? :rw : :w
127
+ selected_monitors << monitor
87
128
  end
88
-
89
- results
90
129
  end
91
- end
92
130
 
93
- # Select for ready monitors, successively yielding each one in a block
94
- def select_each(timeout = nil, &block)
95
- selected = select(timeout)
96
- return unless selected
97
- selected.each(&block)
98
- selected.size
131
+ if block_given?
132
+ selected_monitors.each { |m| yield m }
133
+ selected_monitors.size
134
+ else
135
+ selected_monitors.to_a
136
+ end
99
137
  end
100
138
 
101
- # Wake up other threads waiting on this selector
139
+ # Wake up a thread that's in the middle of selecting on this selector, if
140
+ # any such thread exists.
141
+ #
142
+ # Invoking this method more than once between two successive select calls
143
+ # has the same effect as invoking it just once. In other words, it provides
144
+ # level-triggered behavior.
102
145
  def wakeup
103
146
  # Send the selector a signal in the form of writing data to a pipe
104
- @waker << "\0"
147
+ begin
148
+ @waker.write_nonblock "\0"
149
+ rescue IO::WaitWritable
150
+ # This indicates the wakeup pipe is full, which means the other thread
151
+ # has already received many wakeup calls, but not processed them yet.
152
+ # The other thread will completely drain this pipe when it wakes up,
153
+ # so it's ok to ignore this exception if it occurs: we know the other
154
+ # thread has already been signaled to wake up
155
+ end
156
+
105
157
  nil
106
158
  end
107
159
 
@@ -110,13 +162,27 @@ module NIO
110
162
  @lock.synchronize do
111
163
  return if @closed
112
164
 
113
- @wakeup.close rescue nil
114
- @waker.close rescue nil
165
+ begin
166
+ @wakeup.close
167
+ rescue IOError
168
+ end
169
+
170
+ begin
171
+ @waker.close
172
+ rescue IOError
173
+ end
174
+
115
175
  @closed = true
116
176
  end
117
177
  end
118
178
 
119
179
  # Is this selector closed?
120
- def closed?; @closed end
180
+ def closed?
181
+ @closed
182
+ end
183
+
184
+ def empty?
185
+ @selectables.empty?
186
+ end
121
187
  end
122
188
  end
data/lib/nio/version.rb CHANGED
@@ -1,3 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2011-2018, by Tony Arcieri.
5
+ # Copyright, 2018-2024, by Samuel Williams.
6
+ # Copyright, 2023, by Tsimnuj Hawj.
7
+
1
8
  module NIO
2
- VERSION = "0.2.0"
9
+ VERSION = "2.7.5"
3
10
  end
data/lib/nio.rb CHANGED
@@ -1,5 +1,16 @@
1
- require 'thread'
2
- require 'nio/version'
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2011-2017, by Tony Arcieri.
5
+ # Copyright, 2013, by Stephen von Takach.
6
+ # Copyright, 2013, by Per Lundberg.
7
+ # Copyright, 2014, by Marek Kowalcze.
8
+ # Copyright, 2016, by Upekshe Jayasekera.
9
+ # Copyright, 2019-2023, by Samuel Williams.
10
+ # Copyright, 2021, by Jun Jiang.
11
+
12
+ require "socket"
13
+ require "nio/version"
3
14
 
4
15
  # New I/O for Ruby
5
16
  module NIO
@@ -7,24 +18,44 @@ module NIO
7
18
  # * select: in pure Ruby using Kernel.select
8
19
  # * libev: as a C extension using libev
9
20
  # * java: using Java NIO
10
- def self.engine; ENGINE end
21
+ def self.engine
22
+ ENGINE
23
+ end
24
+
25
+ def self.pure?(env = ENV)
26
+ # The user has explicitly opted in to non-native implementation:
27
+ if env["NIO4R_PURE"] == "true"
28
+ return true
29
+ end
30
+
31
+ # Native Ruby on Windows is not supported:
32
+ if (Gem.win_platform? && !defined?(JRUBY_VERSION))
33
+ return true
34
+ end
35
+
36
+ # M1 native extension is crashing on M1 (arm64):
37
+ # if RUBY_PLATFORM =~ /darwin/ && RUBY_PLATFORM =~ /arm64/
38
+ # return true
39
+ # end
40
+
41
+ return false
42
+ end
11
43
  end
12
44
 
13
- if ENV["NIO4R_PURE"]
14
- require 'nio/monitor'
15
- require 'nio/selector'
16
- NIO::ENGINE = 'select'
45
+ if NIO.pure?
46
+ require "nio/monitor"
47
+ require "nio/selector"
48
+ require "nio/bytebuffer"
49
+ NIO::ENGINE = "ruby"
17
50
  else
51
+ require "nio4r_ext"
52
+
18
53
  if defined?(JRUBY_VERSION)
19
- require 'java'
20
- require 'nio/jruby/monitor'
21
- require 'nio/jruby/selector'
22
- NIO::ENGINE = 'java'
54
+ require "java"
55
+ require "jruby"
56
+ org.nio4r.Nio4r.new.load(JRuby.runtime, false)
57
+ NIO::ENGINE = "java"
23
58
  else
24
- require 'nio4r_ext'
25
- NIO::ENGINE = 'libev'
59
+ NIO::ENGINE = "libev"
26
60
  end
27
61
  end
28
-
29
- # TIMTOWTDI!!!
30
- Nio = NIO
data/lib/nio4r.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Phillip Aldridge.
5
+ # Copyright, 2023, by Samuel Williams.
6
+
7
+ require_relative "nio"