io-event-machty 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,270 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../interrupt'
22
+
23
+ module IO::Event
24
+ module Selector
25
+ class Select
26
+ def initialize(loop)
27
+ @loop = loop
28
+
29
+ @readable = Hash.new.compare_by_identity
30
+ @writable = Hash.new.compare_by_identity
31
+
32
+ @blocked = false
33
+
34
+ @ready = Queue.new
35
+ @interrupt = Interrupt.attach(self)
36
+ end
37
+
38
+ attr :loop
39
+
40
+ # If the event loop is currently blocked,
41
+ def wakeup
42
+ if @blocked
43
+ @interrupt.signal
44
+
45
+ return true
46
+ end
47
+
48
+ return false
49
+ end
50
+
51
+ def close
52
+ @interrupt.close
53
+
54
+ @loop = nil
55
+ @readable = nil
56
+ @writable = nil
57
+ end
58
+
59
+ Optional = Struct.new(:fiber) do
60
+ def transfer(*arguments)
61
+ fiber&.transfer(*arguments)
62
+ end
63
+
64
+ def alive?
65
+ fiber&.alive?
66
+ end
67
+
68
+ def nullify
69
+ self.fiber = nil
70
+ end
71
+ end
72
+
73
+ # Transfer from the current fiber to the event loop.
74
+ def transfer
75
+ @loop.transfer
76
+ end
77
+
78
+ # Transfer from the current fiber to the specified fiber. Put the current fiber into the ready list.
79
+ def resume(fiber, *arguments)
80
+ optional = Optional.new(Fiber.current)
81
+ @ready.push(optional)
82
+
83
+ fiber.transfer(*arguments)
84
+ ensure
85
+ optional.nullify
86
+ end
87
+
88
+ # Yield from the current fiber back to the event loop. Put the current fiber into the ready list.
89
+ def yield
90
+ optional = Optional.new(Fiber.current)
91
+ @ready.push(optional)
92
+
93
+ @loop.transfer
94
+ ensure
95
+ optional.nullify
96
+ end
97
+
98
+ # Append the given fiber into the ready list.
99
+ def push(fiber)
100
+ @ready.push(fiber)
101
+ end
102
+
103
+ # Transfer to the given fiber and raise an exception. Put the current fiber into the ready list.
104
+ def raise(fiber, *arguments)
105
+ optional = Optional.new(Fiber.current)
106
+ @ready.push(optional)
107
+
108
+ fiber.raise(*arguments)
109
+ ensure
110
+ optional.nullify
111
+ end
112
+
113
+ def ready?
114
+ !@ready.empty?
115
+ end
116
+
117
+ def io_wait(fiber, io, events)
118
+ remove_readable = remove_writable = false
119
+
120
+ if (events & IO::READABLE) > 0 or (events & IO::PRIORITY) > 0
121
+ @readable[io] = fiber
122
+ remove_readable = true
123
+ end
124
+
125
+ if (events & IO::WRITABLE) > 0
126
+ @writable[io] = fiber
127
+ remove_writable = true
128
+ end
129
+
130
+ @loop.transfer
131
+ ensure
132
+ @readable.delete(io) if remove_readable
133
+ @writable.delete(io) if remove_writable
134
+ end
135
+
136
+ if IO.const_defined?(:Buffer)
137
+ EAGAIN = Errno::EAGAIN::Errno
138
+
139
+ def io_read(fiber, io, buffer, length)
140
+ offset = 0
141
+
142
+ while true
143
+ maximum_size = buffer.size - offset
144
+
145
+ case result = blocking{io.read_nonblock(maximum_size, exception: false)}
146
+ when :wait_readable
147
+ if length > 0
148
+ self.io_wait(fiber, io, IO::READABLE)
149
+ else
150
+ return -EAGAIN
151
+ end
152
+ when :wait_writable
153
+ if length > 0
154
+ self.io_wait(fiber, io, IO::WRITABLE)
155
+ else
156
+ return -EAGAIN
157
+ end
158
+ when nil
159
+ break
160
+ else
161
+ buffer.set_string(result, offset)
162
+
163
+ size = result.bytesize
164
+ offset += size
165
+ break if size >= length
166
+ length -= size
167
+ end
168
+ end
169
+
170
+ return offset
171
+ end
172
+
173
+ def io_write(fiber, io, buffer, length)
174
+ offset = 0
175
+
176
+ while true
177
+ maximum_size = buffer.size - offset
178
+
179
+ chunk = buffer.get_string(offset, maximum_size)
180
+ case result = blocking{io.write_nonblock(chunk, exception: false)}
181
+ when :wait_readable
182
+ if length > 0
183
+ self.io_wait(fiber, io, IO::READABLE)
184
+ else
185
+ return -EAGAIN
186
+ end
187
+ when :wait_writable
188
+ if length > 0
189
+ self.io_wait(fiber, io, IO::WRITABLE)
190
+ else
191
+ return -EAGAIN
192
+ end
193
+ else
194
+ offset += result
195
+ break if result >= length
196
+ length -= result
197
+ end
198
+ end
199
+
200
+ return offset
201
+ end
202
+ end
203
+
204
+ def process_wait(fiber, pid, flags)
205
+ r, w = IO.pipe
206
+
207
+ thread = Thread.new do
208
+ Process::Status.wait(pid, flags)
209
+ ensure
210
+ w.close
211
+ end
212
+
213
+ self.io_wait(fiber, r, IO::READABLE)
214
+
215
+ return thread.value
216
+ ensure
217
+ r.close
218
+ w.close
219
+ thread&.kill
220
+ end
221
+
222
+ private def pop_ready
223
+ unless @ready.empty?
224
+ count = @ready.size
225
+
226
+ count.times do
227
+ fiber = @ready.pop
228
+ fiber.transfer if fiber.alive?
229
+ end
230
+
231
+ return true
232
+ end
233
+ end
234
+
235
+ def select(duration = nil)
236
+ if pop_ready
237
+ # If we have popped items from the ready list, they may influence the duration calculation, so we don't delay the event loop:
238
+ duration = 0
239
+ end
240
+
241
+ @blocked = true
242
+ duration = 0 unless @ready.empty?
243
+ readable, writable, _ = ::IO.select(@readable.keys, @writable.keys, nil, duration)
244
+ @blocked = false
245
+
246
+ ready = Hash.new(0)
247
+
248
+ readable&.each do |io|
249
+ fiber = @readable.delete(io)
250
+ ready[fiber] |= IO::READABLE
251
+ end
252
+
253
+ writable&.each do |io|
254
+ fiber = @writable.delete(io)
255
+ ready[fiber] |= IO::WRITABLE
256
+ end
257
+
258
+ ready.each do |fiber, events|
259
+ fiber.transfer(events) if fiber.alive?
260
+ end
261
+
262
+ return ready.size
263
+ end
264
+
265
+ private def blocking(&block)
266
+ Fiber.new(blocking: true, &block).resume
267
+ end
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'selector/select'
22
+ require_relative 'debug/selector'
23
+
24
+ module IO::Event
25
+ module Selector
26
+ def self.default(env = ENV)
27
+ if name = env['IO_EVENT_SELECTOR']&.to_sym
28
+ if const_defined?(name)
29
+ return const_get(name)
30
+ else
31
+ warn "Could not find IO_EVENT_SELECTOR=#{name}!"
32
+ end
33
+ end
34
+
35
+ if self.const_defined?(:EPoll)
36
+ return EPoll
37
+ elsif self.const_defined?(:KQueue)
38
+ return KQueue
39
+ else
40
+ return Select
41
+ end
42
+ end
43
+
44
+ def self.new(loop, env = ENV)
45
+ selector = default(env).new(loop)
46
+
47
+ if debug = env['IO_EVENT_DEBUG_SELECTOR']
48
+ selector = Debug::Selector.new(selector)
49
+ end
50
+
51
+ return selector
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module IO::Event
22
+ VERSION = "1.0.1"
23
+ end
data/lib/io/event.rb ADDED
@@ -0,0 +1,29 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'event/version'
22
+ require_relative 'event/selector'
23
+
24
+ begin
25
+ require 'IO_Event'
26
+ rescue LoadError => error
27
+ warn "Could not load native event selector: #{error}"
28
+ # Ignore.
29
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: io-event-machty
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: covered
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sus
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.6'
69
+ description:
70
+ email:
71
+ executables: []
72
+ extensions:
73
+ - ext/extconf.rb
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ext/IO_Event.bundle
77
+ - ext/Makefile
78
+ - ext/event.o
79
+ - ext/extconf.h
80
+ - ext/extconf.rb
81
+ - ext/interrupt.o
82
+ - ext/io/event/event.c
83
+ - ext/io/event/event.h
84
+ - ext/io/event/interrupt.c
85
+ - ext/io/event/interrupt.h
86
+ - ext/io/event/selector/epoll.c
87
+ - ext/io/event/selector/epoll.h
88
+ - ext/io/event/selector/kqueue.c
89
+ - ext/io/event/selector/kqueue.h
90
+ - ext/io/event/selector/pidfd.c
91
+ - ext/io/event/selector/selector.c
92
+ - ext/io/event/selector/selector.h
93
+ - ext/io/event/selector/uring.c
94
+ - ext/io/event/selector/uring.h
95
+ - ext/kqueue.o
96
+ - ext/mkmf.log
97
+ - ext/selector.o
98
+ - lib/io/event.rb
99
+ - lib/io/event/debug/selector.rb
100
+ - lib/io/event/interrupt.rb
101
+ - lib/io/event/selector.rb
102
+ - lib/io/event/selector/select.rb
103
+ - lib/io/event/version.rb
104
+ homepage: https://github.com/socketry/io-event
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 3.3.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: An event loop.
127
+ test_files: []