ed-precompiled_nio4r 2.7.4-arm64-darwin
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 +7 -0
- data/ext/libev/Changes +617 -0
- data/ext/libev/LICENSE +37 -0
- data/ext/libev/README +59 -0
- data/ext/libev/ev.c +5689 -0
- data/ext/libev/ev.h +859 -0
- data/ext/libev/ev_epoll.c +298 -0
- data/ext/libev/ev_iouring.c +694 -0
- data/ext/libev/ev_kqueue.c +224 -0
- data/ext/libev/ev_linuxaio.c +620 -0
- data/ext/libev/ev_poll.c +156 -0
- data/ext/libev/ev_port.c +192 -0
- data/ext/libev/ev_select.c +316 -0
- data/ext/libev/ev_vars.h +249 -0
- data/ext/libev/ev_win32.c +162 -0
- data/ext/libev/ev_wrap.h +272 -0
- data/ext/nio4r/.clang-format +16 -0
- data/ext/nio4r/bytebuffer.c +465 -0
- data/ext/nio4r/extconf.rb +53 -0
- data/ext/nio4r/libev.h +7 -0
- data/ext/nio4r/monitor.c +344 -0
- data/ext/nio4r/nio4r.h +48 -0
- data/ext/nio4r/nio4r_ext.c +24 -0
- data/ext/nio4r/org/nio4r/ByteBuffer.java +295 -0
- data/ext/nio4r/org/nio4r/Monitor.java +176 -0
- data/ext/nio4r/org/nio4r/Nio4r.java +104 -0
- data/ext/nio4r/org/nio4r/Selector.java +297 -0
- data/ext/nio4r/selector.c +606 -0
- data/lib/3.0/nio4r_ext.bundle +0 -0
- data/lib/3.1/nio4r_ext.bundle +0 -0
- data/lib/3.2/nio4r_ext.bundle +0 -0
- data/lib/3.3/nio4r_ext.bundle +0 -0
- data/lib/3.4/nio4r_ext.bundle +0 -0
- data/lib/nio/bytebuffer.rb +235 -0
- data/lib/nio/monitor.rb +124 -0
- data/lib/nio/selector.rb +188 -0
- data/lib/nio/version.rb +10 -0
- data/lib/nio.rb +66 -0
- data/lib/nio4r.rb +7 -0
- data/license.md +80 -0
- data/readme.md +91 -0
- data/releases.md +343 -0
- metadata +138 -0
|
@@ -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
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
|
|
10
|
+
module NIO
|
|
11
|
+
# Monitors watch IO objects for specific events
|
|
12
|
+
class Monitor
|
|
13
|
+
attr_reader :io, :interests, :selector
|
|
14
|
+
attr_accessor :value, :readiness
|
|
15
|
+
|
|
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
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Is the IO object readable?
|
|
103
|
+
def readable?
|
|
104
|
+
readiness == :r || readiness == :rw
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Is the IO object writable?
|
|
108
|
+
def writable?
|
|
109
|
+
readiness == :w || readiness == :rw
|
|
110
|
+
end
|
|
111
|
+
alias writeable? writable?
|
|
112
|
+
|
|
113
|
+
# Is this monitor closed?
|
|
114
|
+
def closed?
|
|
115
|
+
@closed
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Deactivate this monitor
|
|
119
|
+
def close(deregister = true)
|
|
120
|
+
@closed = true
|
|
121
|
+
@selector.deregister(io) if deregister
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/nio/selector.rb
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
|
|
20
|
+
module NIO
|
|
21
|
+
# Selectors monitor IO objects for events of interest
|
|
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
|
+
|
|
30
|
+
# Create a new NIO::Selector
|
|
31
|
+
def initialize(backend = :ruby)
|
|
32
|
+
raise ArgumentError, "unsupported backend: #{backend}" unless [:ruby, nil].include?(backend)
|
|
33
|
+
|
|
34
|
+
@selectables = {}
|
|
35
|
+
@lock = Mutex.new
|
|
36
|
+
|
|
37
|
+
# Other threads can wake up a selector
|
|
38
|
+
@wakeup, @waker = IO.pipe
|
|
39
|
+
@closed = false
|
|
40
|
+
end
|
|
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
|
+
|
|
58
|
+
# Register interest in an IO object with the selector for the given types
|
|
59
|
+
# of events. Valid event types for interest are:
|
|
60
|
+
# * :r - is the IO readable?
|
|
61
|
+
# * :w - is the IO writeable?
|
|
62
|
+
# * :rw - is the IO either readable or writeable?
|
|
63
|
+
def register(io, interest)
|
|
64
|
+
unless defined?(::OpenSSL) && io.is_a?(::OpenSSL::SSL::SSLSocket)
|
|
65
|
+
io = IO.try_convert(io)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@lock.synchronize do
|
|
69
|
+
raise IOError, "selector is closed" if closed?
|
|
70
|
+
|
|
71
|
+
monitor = @selectables[io]
|
|
72
|
+
raise ArgumentError, "already registered as #{monitor.interests.inspect}" if monitor
|
|
73
|
+
|
|
74
|
+
monitor = Monitor.new(io, interest, self)
|
|
75
|
+
@selectables[monitor.io] = monitor
|
|
76
|
+
|
|
77
|
+
monitor
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Deregister the given IO object from the selector
|
|
82
|
+
def deregister(io)
|
|
83
|
+
@lock.synchronize do
|
|
84
|
+
monitor = @selectables.delete IO.try_convert(io)
|
|
85
|
+
monitor.close(false) if monitor && !monitor.closed?
|
|
86
|
+
monitor
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Is the given IO object registered with the selector?
|
|
91
|
+
def registered?(io)
|
|
92
|
+
@lock.synchronize { @selectables.key? io }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Select which monitors are ready
|
|
96
|
+
def select(timeout = nil)
|
|
97
|
+
selected_monitors = Set.new
|
|
98
|
+
|
|
99
|
+
@lock.synchronize do
|
|
100
|
+
readers = [@wakeup]
|
|
101
|
+
writers = []
|
|
102
|
+
|
|
103
|
+
@selectables.each do |io, monitor|
|
|
104
|
+
readers << io if monitor.interests == :r || monitor.interests == :rw
|
|
105
|
+
writers << io if monitor.interests == :w || monitor.interests == :rw
|
|
106
|
+
monitor.readiness = nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
ready_readers, ready_writers = Kernel.select(readers, writers, [], timeout)
|
|
110
|
+
return unless ready_readers # timeout
|
|
111
|
+
|
|
112
|
+
ready_readers.each do |io|
|
|
113
|
+
if io == @wakeup
|
|
114
|
+
# Clear all wakeup signals we've received by reading them
|
|
115
|
+
# Wakeups should have level triggered behavior
|
|
116
|
+
@wakeup.read(@wakeup.stat.size)
|
|
117
|
+
else
|
|
118
|
+
monitor = @selectables[io]
|
|
119
|
+
monitor.readiness = :r
|
|
120
|
+
selected_monitors << monitor
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
ready_writers.each do |io|
|
|
125
|
+
monitor = @selectables[io]
|
|
126
|
+
monitor.readiness = monitor.readiness == :r ? :rw : :w
|
|
127
|
+
selected_monitors << monitor
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if block_given?
|
|
132
|
+
selected_monitors.each { |m| yield m }
|
|
133
|
+
selected_monitors.size
|
|
134
|
+
else
|
|
135
|
+
selected_monitors.to_a
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
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.
|
|
145
|
+
def wakeup
|
|
146
|
+
# Send the selector a signal in the form of writing data to a pipe
|
|
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
|
+
|
|
157
|
+
nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Close this selector and free its resources
|
|
161
|
+
def close
|
|
162
|
+
@lock.synchronize do
|
|
163
|
+
return if @closed
|
|
164
|
+
|
|
165
|
+
begin
|
|
166
|
+
@wakeup.close
|
|
167
|
+
rescue IOError
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
@waker.close
|
|
172
|
+
rescue IOError
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
@closed = true
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Is this selector closed?
|
|
180
|
+
def closed?
|
|
181
|
+
@closed
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def empty?
|
|
185
|
+
@selectables.empty?
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
data/lib/nio/version.rb
ADDED
data/lib/nio.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
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"
|
|
14
|
+
|
|
15
|
+
# New I/O for Ruby
|
|
16
|
+
module NIO
|
|
17
|
+
# NIO implementation, one of the following (as a string):
|
|
18
|
+
# * select: in pure Ruby using Kernel.select
|
|
19
|
+
# * libev: as a C extension using libev
|
|
20
|
+
# * java: using Java NIO
|
|
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
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if NIO.pure?
|
|
46
|
+
require "nio/monitor"
|
|
47
|
+
require "nio/selector"
|
|
48
|
+
require "nio/bytebuffer"
|
|
49
|
+
NIO::ENGINE = "ruby"
|
|
50
|
+
else
|
|
51
|
+
begin
|
|
52
|
+
ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION)
|
|
53
|
+
require "#{ruby_version}/nio4r_ext"
|
|
54
|
+
rescue LoadError
|
|
55
|
+
require "nio4r_ext"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if defined?(JRUBY_VERSION)
|
|
59
|
+
require "java"
|
|
60
|
+
require "jruby"
|
|
61
|
+
org.nio4r.Nio4r.new.load(JRuby.runtime, false)
|
|
62
|
+
NIO::ENGINE = "java"
|
|
63
|
+
else
|
|
64
|
+
NIO::ENGINE = "libev"
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/nio4r.rb
ADDED
data/license.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright, 2011-2020, by Tony Arcieri.
|
|
4
|
+
Copyright, 2012, by Bernd Ahlers.
|
|
5
|
+
Copyright, 2012, by Logan Bowers.
|
|
6
|
+
Copyright, 2012, by Dirkjan Bussink.
|
|
7
|
+
Copyright, 2013, by Sadayuki Furuhashi.
|
|
8
|
+
Copyright, 2013, by Shannon Skipper.
|
|
9
|
+
Copyright, 2013, by Stephen von Takach.
|
|
10
|
+
Copyright, 2013, by Tim Carey-Smith.
|
|
11
|
+
Copyright, 2013, by Per Lundberg.
|
|
12
|
+
Copyright, 2013, by Ravil Bayramgalin.
|
|
13
|
+
Copyright, 2013, by Luis Lavena.
|
|
14
|
+
Copyright, 2014, by Anatol Pomozov.
|
|
15
|
+
Copyright, 2014, by Hiroshi Shibata.
|
|
16
|
+
Copyright, 2014, by Marek Kowalcze.
|
|
17
|
+
Copyright, 2014, by Sergey Avseyev.
|
|
18
|
+
Copyright, 2014, by John Thornton.
|
|
19
|
+
Copyright, 2015-2017, by Tiago Cardoso.
|
|
20
|
+
Copyright, 2015, by Daniel Berger.
|
|
21
|
+
Copyright, 2015-2016, by Upekshe Jayasekera.
|
|
22
|
+
Copyright, 2015, by Vladimir Kochnev.
|
|
23
|
+
Copyright, 2016-2018, by Jun Aruga.
|
|
24
|
+
Copyright, 2016, by Omer Katz.
|
|
25
|
+
Copyright, 2016, by Denis Washington.
|
|
26
|
+
Copyright, 2016-2021, by Olle Jonsson.
|
|
27
|
+
Copyright, 2017, by Tao Luo.
|
|
28
|
+
Copyright, 2017, by Usaku Nakamura.
|
|
29
|
+
Copyright, 2017-2022, by Gregory Longtin.
|
|
30
|
+
Copyright, 2017, by Lars Kanis.
|
|
31
|
+
Copyright, 2017, by Tomoya Ishida.
|
|
32
|
+
Copyright, 2018-2024, by Samuel Williams.
|
|
33
|
+
Copyright, 2019, by Cédric Boutillier.
|
|
34
|
+
Copyright, 2019-2020, by Benoit Daloze.
|
|
35
|
+
Copyright, 2019, by Jesús Burgos Maciá.
|
|
36
|
+
Copyright, 2019, by Thomas Kuntz.
|
|
37
|
+
Copyright, 2019, by Orien Madgwick.
|
|
38
|
+
Copyright, 2019, by Zhang Kang.
|
|
39
|
+
Copyright, 2020, by Thomas Dziedzic.
|
|
40
|
+
Copyright, 2020, by Elad Eyal.
|
|
41
|
+
Copyright, 2020, by Pedro Paiva.
|
|
42
|
+
Copyright, 2020, by Boaz Segev.
|
|
43
|
+
Copyright, 2020, by Charles Oliver Nutter.
|
|
44
|
+
Copyright, 2020-2021, by Joao Fernandes.
|
|
45
|
+
Copyright, 2021, by Jun Jiang.
|
|
46
|
+
Copyright, 2021, by Pavel Lobashov.
|
|
47
|
+
Copyright, 2021, by Jeffrey Martin.
|
|
48
|
+
Copyright, 2023-2024, by Pavel Rosický.
|
|
49
|
+
Copyright, 2023, by Tsimnuj Hawj.
|
|
50
|
+
Copyright, 2023, by Phillip Aldridge.
|
|
51
|
+
Copyright, 2023, by Maxime Demolin.
|
|
52
|
+
Copyright, 2023-2024, by Vít Ondruch.
|
|
53
|
+
Copyright, 2023, by Jean Boussier.
|
|
54
|
+
|
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
56
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
57
|
+
in the Software without restriction, including without limitation the rights
|
|
58
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
59
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
60
|
+
furnished to do so, subject to the following conditions:
|
|
61
|
+
|
|
62
|
+
The above copyright notice and this permission notice shall be included in all
|
|
63
|
+
copies or substantial portions of the Software.
|
|
64
|
+
|
|
65
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
66
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
67
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
68
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
69
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
70
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
71
|
+
SOFTWARE.
|
|
72
|
+
|
|
73
|
+
## libev
|
|
74
|
+
|
|
75
|
+
Released under the BSD-2-Clause OR GPL-2.0-or-later license.
|
|
76
|
+
See [ext/libev/LICENSE] for details.
|
|
77
|
+
|
|
78
|
+
Copyright, 2007-2019, by Marc Alexander Lehmann.
|
|
79
|
+
|
|
80
|
+
[ext/libev/LICENSE]: https://github.com/socketry/nio4r/blob/master/ext/libev/LICENSE
|