io-event 1.6.5 → 1.9.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/extconf.rb +26 -13
- data/ext/io/event/{selector/array.h → array.h} +74 -18
- data/ext/io/event/event.c +9 -26
- data/ext/io/event/event.h +2 -19
- data/ext/io/event/fiber.c +63 -0
- data/ext/io/event/fiber.h +23 -0
- data/ext/io/event/interrupt.c +19 -27
- data/ext/io/event/interrupt.h +2 -19
- data/ext/io/event/{selector/list.h → list.h} +20 -2
- data/ext/io/event/profiler.c +505 -0
- data/ext/io/event/profiler.h +8 -0
- data/ext/io/event/selector/epoll.c +41 -43
- data/ext/io/event/selector/epoll.h +2 -19
- data/ext/io/event/selector/kqueue.c +34 -41
- data/ext/io/event/selector/kqueue.h +2 -19
- data/ext/io/event/selector/pidfd.c +2 -19
- data/ext/io/event/selector/selector.c +65 -102
- data/ext/io/event/selector/selector.h +51 -46
- data/ext/io/event/selector/uring.c +130 -47
- data/ext/io/event/selector/uring.h +2 -19
- data/ext/io/event/time.c +35 -0
- data/ext/io/event/time.h +17 -0
- data/lib/io/event/debug/selector.rb +44 -4
- data/lib/io/event/interrupt.rb +2 -2
- data/lib/io/event/native.rb +11 -0
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/profiler.rb +18 -0
- data/lib/io/event/selector/nonblock.rb +6 -2
- data/lib/io/event/selector/select.rb +25 -5
- data/lib/io/event/selector.rb +15 -5
- data/lib/io/event/support.rb +23 -2
- data/lib/io/event/timers.rb +42 -4
- data/lib/io/event/version.rb +4 -2
- data/lib/io/event.rb +5 -11
- data/license.md +4 -1
- data/readme.md +22 -4
- data/releases.md +57 -0
- data.tar.gz.sig +0 -0
- metadata +20 -11
- metadata.gz.sig +0 -0
|
@@ -10,6 +10,7 @@ class IO
|
|
|
10
10
|
# of its contents to determine priority.
|
|
11
11
|
# See <https://en.wikipedia.org/wiki/Binary_heap> for explanations of the main methods.
|
|
12
12
|
class PriorityHeap
|
|
13
|
+
# Initializes the heap.
|
|
13
14
|
def initialize
|
|
14
15
|
# The heap is represented with an array containing a binary tree. See
|
|
15
16
|
# https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
|
|
@@ -17,18 +18,19 @@ class IO
|
|
|
17
18
|
@contents = []
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
+
# @returns [Object | Nil] the smallest element in the heap without removing it, or nil if the heap is empty.
|
|
21
22
|
def peek
|
|
22
23
|
@contents[0]
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
#
|
|
26
|
+
# @returns [Integer] the number of elements in the heap.
|
|
26
27
|
def size
|
|
27
28
|
@contents.size
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
#
|
|
31
|
-
#
|
|
31
|
+
# Removes and returns the smallest element in the heap, or nil if the heap is empty.
|
|
32
|
+
#
|
|
33
|
+
# @returns [Object | Nil] The smallest element in the heap, or nil if the heap is empty.
|
|
32
34
|
def pop
|
|
33
35
|
# If the heap is empty:
|
|
34
36
|
if @contents.empty?
|
|
@@ -57,7 +59,9 @@ class IO
|
|
|
57
59
|
return value
|
|
58
60
|
end
|
|
59
61
|
|
|
60
|
-
#
|
|
62
|
+
# Add a new element to the heap, then rearrange elements until the heap invariant is true again.
|
|
63
|
+
#
|
|
64
|
+
# @parameter element [Object] The element to add to the heap.
|
|
61
65
|
def push(element)
|
|
62
66
|
# Insert the item at the end of the heap:
|
|
63
67
|
@contents.push(element)
|
|
@@ -75,10 +79,9 @@ class IO
|
|
|
75
79
|
@contents = []
|
|
76
80
|
end
|
|
77
81
|
|
|
78
|
-
# Validate the heap invariant. Every element except the root must not be smaller than
|
|
79
|
-
# its parent element. Note that it MAY be equal.
|
|
82
|
+
# Validate the heap invariant. Every element except the root must not be smaller than its parent element. Note that it MAY be equal.
|
|
80
83
|
def valid?
|
|
81
|
-
#
|
|
84
|
+
# Notice we skip index 0 on purpose, because it has no parent
|
|
82
85
|
(1..(@contents.size - 1)).all? { |e| @contents[e] >= @contents[(e - 1) / 2] }
|
|
83
86
|
end
|
|
84
87
|
|
|
@@ -93,10 +96,7 @@ class IO
|
|
|
93
96
|
parent_index = (index - 1) / 2 # watch out, integer division!
|
|
94
97
|
|
|
95
98
|
while index > 0 && @contents[index] < @contents[parent_index]
|
|
96
|
-
#
|
|
97
|
-
# to uphold the minheap invariant and update the index of the 'current'
|
|
98
|
-
# node. If the node is already at index 0, we can also stop because that
|
|
99
|
-
# is the root of the heap.
|
|
99
|
+
# If the node has a smaller value than its parent, swap these nodes to uphold the minheap invariant and update the index of the 'current' node. If the node is already at index 0, we can also stop because that is the root of the heap.
|
|
100
100
|
# swap(index, parent_index)
|
|
101
101
|
@contents[index], @contents[parent_index] = @contents[parent_index], @contents[index]
|
|
102
102
|
|
|
@@ -114,8 +114,7 @@ class IO
|
|
|
114
114
|
left_value = @contents[left_index]
|
|
115
115
|
|
|
116
116
|
if left_value.nil?
|
|
117
|
-
# This node has no children so it can't bubble down any further.
|
|
118
|
-
# We're done here!
|
|
117
|
+
# This node has no children so it can't bubble down any further. We're done here!
|
|
119
118
|
return
|
|
120
119
|
end
|
|
121
120
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "native"
|
|
7
|
+
|
|
8
|
+
module IO::Event
|
|
9
|
+
unless self.const_defined?(:Profiler)
|
|
10
|
+
module Profiler
|
|
11
|
+
# The default profiler, if the platform supports it.
|
|
12
|
+
# Use `IO_EVENT_PROFILER=true` to enable it.
|
|
13
|
+
def self.default
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2022-
|
|
4
|
+
# Copyright, 2022-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require
|
|
6
|
+
require "io/nonblock"
|
|
7
7
|
|
|
8
8
|
module IO::Event
|
|
9
9
|
module Selector
|
|
10
|
+
# Execute the given block in non-blocking mode.
|
|
11
|
+
#
|
|
12
|
+
# @parameter io [IO] The IO object to operate on.
|
|
13
|
+
# @yields {...} The block to execute.
|
|
10
14
|
def self.nonblock(io, &block)
|
|
11
15
|
io.nonblock(&block)
|
|
12
16
|
rescue Errno::EBADF
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
|
5
5
|
# Copyright, 2023, by Math Ieu.
|
|
6
6
|
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
7
|
+
require_relative "../interrupt"
|
|
8
|
+
require_relative "../support"
|
|
9
9
|
|
|
10
10
|
module IO::Event
|
|
11
11
|
module Selector
|
|
12
|
+
# A pure-Ruby implementation of the event selector.
|
|
12
13
|
class Select
|
|
14
|
+
# Initialize the selector with the given event loop fiber.
|
|
13
15
|
def initialize(loop)
|
|
14
16
|
@loop = loop
|
|
15
17
|
|
|
@@ -23,12 +25,13 @@ module IO::Event
|
|
|
23
25
|
@idle_duration = 0.0
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
# @attribute [Fiber] The event loop fiber.
|
|
26
29
|
attr :loop
|
|
27
30
|
|
|
28
|
-
# This is the amount of time the event loop was idle during the last select call.
|
|
31
|
+
# @attribute [Float] This is the amount of time the event loop was idle during the last select call.
|
|
29
32
|
attr :idle_duration
|
|
30
33
|
|
|
31
|
-
#
|
|
34
|
+
# Wake up the event loop if it is currently sleeping.
|
|
32
35
|
def wakeup
|
|
33
36
|
if @blocked
|
|
34
37
|
@interrupt.signal
|
|
@@ -39,6 +42,7 @@ module IO::Event
|
|
|
39
42
|
return false
|
|
40
43
|
end
|
|
41
44
|
|
|
45
|
+
# Close the selector and release any resources.
|
|
42
46
|
def close
|
|
43
47
|
@interrupt.close
|
|
44
48
|
|
|
@@ -100,6 +104,7 @@ module IO::Event
|
|
|
100
104
|
optional.nullify
|
|
101
105
|
end
|
|
102
106
|
|
|
107
|
+
# @returns [Boolean] Whether the ready list is not empty, i.e. there are fibers ready to be resumed.
|
|
103
108
|
def ready?
|
|
104
109
|
!@ready.empty?
|
|
105
110
|
end
|
|
@@ -144,6 +149,11 @@ module IO::Event
|
|
|
144
149
|
end
|
|
145
150
|
end
|
|
146
151
|
|
|
152
|
+
# Wait for the given IO to become readable or writable.
|
|
153
|
+
#
|
|
154
|
+
# @parameter fiber [Fiber] The fiber that is waiting.
|
|
155
|
+
# @parameter io [IO] The IO object to wait on.
|
|
156
|
+
# @parameter events [Integer] The events to wait for.
|
|
147
157
|
def io_wait(fiber, io, events)
|
|
148
158
|
waiter = @waiting[io] = Waiter.new(fiber, events, @waiting[io])
|
|
149
159
|
|
|
@@ -152,6 +162,11 @@ module IO::Event
|
|
|
152
162
|
waiter&.invalidate
|
|
153
163
|
end
|
|
154
164
|
|
|
165
|
+
# Wait for multiple IO objects to become readable or writable.
|
|
166
|
+
#
|
|
167
|
+
# @parameter readable [Array(IO)] The list of IO objects to wait for readability.
|
|
168
|
+
# @parameter writable [Array(IO)] The list of IO objects to wait for writability.
|
|
169
|
+
# @parameter priority [Array(IO)] The list of IO objects to wait for priority events.
|
|
155
170
|
def io_select(readable, writable, priority, timeout)
|
|
156
171
|
Thread.new do
|
|
157
172
|
IO.select(readable, writable, priority, timeout)
|
|
@@ -161,13 +176,16 @@ module IO::Event
|
|
|
161
176
|
EAGAIN = -Errno::EAGAIN::Errno
|
|
162
177
|
EWOULDBLOCK = -Errno::EWOULDBLOCK::Errno
|
|
163
178
|
|
|
164
|
-
|
|
179
|
+
# Whether the given error code indicates that the operation should be retried.
|
|
180
|
+
protected def again?(errno)
|
|
165
181
|
errno == EAGAIN or errno == EWOULDBLOCK
|
|
166
182
|
end
|
|
167
183
|
|
|
168
184
|
if Support.fiber_scheduler_v3?
|
|
169
185
|
# Ruby 3.3+, full IO::Buffer support.
|
|
170
186
|
|
|
187
|
+
# Read from the given IO to the buffer.
|
|
188
|
+
#
|
|
171
189
|
# @parameter length [Integer] The minimum number of bytes to read.
|
|
172
190
|
# @parameter offset [Integer] The offset into the buffer to read to.
|
|
173
191
|
def io_read(fiber, io, buffer, length, offset = 0)
|
|
@@ -196,6 +214,8 @@ module IO::Event
|
|
|
196
214
|
return total
|
|
197
215
|
end
|
|
198
216
|
|
|
217
|
+
# Write to the given IO from the buffer.
|
|
218
|
+
#
|
|
199
219
|
# @parameter length [Integer] The minimum number of bytes to write.
|
|
200
220
|
# @parameter offset [Integer] The offset into the buffer to write from.
|
|
201
221
|
def io_write(fiber, io, buffer, length, offset = 0)
|
data/lib/io/event/selector.rb
CHANGED
|
@@ -3,14 +3,19 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
6
|
+
require_relative "selector/select"
|
|
7
|
+
require_relative "debug/selector"
|
|
8
|
+
require_relative "support"
|
|
9
9
|
|
|
10
10
|
module IO::Event
|
|
11
|
+
# @namespace
|
|
11
12
|
module Selector
|
|
13
|
+
# The default selector implementation, which is chosen based on the environment and available implementations.
|
|
14
|
+
#
|
|
15
|
+
# @parameter env [Hash] The environment to read configuration from.
|
|
16
|
+
# @returns [Class] The default selector implementation.
|
|
12
17
|
def self.default(env = ENV)
|
|
13
|
-
if name = env[
|
|
18
|
+
if name = env["IO_EVENT_SELECTOR"]&.to_sym
|
|
14
19
|
return const_get(name)
|
|
15
20
|
end
|
|
16
21
|
|
|
@@ -25,10 +30,15 @@ module IO::Event
|
|
|
25
30
|
end
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
# Create a new selector instance, according to the best available implementation.
|
|
34
|
+
#
|
|
35
|
+
# @parameter loop [Fiber] The event loop fiber.
|
|
36
|
+
# @parameter env [Hash] The environment to read configuration from.
|
|
37
|
+
# @returns [Selector] The new selector instance.
|
|
28
38
|
def self.new(loop, env = ENV)
|
|
29
39
|
selector = default(env).new(loop)
|
|
30
40
|
|
|
31
|
-
if debug = env[
|
|
41
|
+
if debug = env["IO_EVENT_DEBUG_SELECTOR"]
|
|
32
42
|
selector = Debug::Selector.wrap(selector, env)
|
|
33
43
|
end
|
|
34
44
|
|
data/lib/io/event/support.rb
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2022-
|
|
4
|
+
# Copyright, 2022-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
class IO
|
|
7
7
|
module Event
|
|
8
|
+
# Helper methods for detecting support for various features.
|
|
8
9
|
module Support
|
|
10
|
+
# Some features are only availble if the IO::Buffer class is available.
|
|
11
|
+
#
|
|
12
|
+
# @returns [Boolean] Whether the IO::Buffer class is available.
|
|
9
13
|
def self.buffer?
|
|
10
14
|
IO.const_defined?(:Buffer)
|
|
11
15
|
end
|
|
12
16
|
|
|
17
|
+
# The basic fiber scheduler was introduced along side the IO::Buffer class.
|
|
18
|
+
#
|
|
19
|
+
# @returns [Boolean] Whether the IO::Buffer class is available.
|
|
20
|
+
#
|
|
21
|
+
# To be removed on 31 Mar 2025.
|
|
13
22
|
def self.fiber_scheduler_v1?
|
|
14
|
-
IO.const_defined?(:Buffer)
|
|
23
|
+
IO.const_defined?(:Buffer)
|
|
15
24
|
end
|
|
16
25
|
|
|
26
|
+
# More advanced read/write methods and blocking controls were introduced in Ruby 3.2.
|
|
27
|
+
#
|
|
28
|
+
# To be removed on 31 Mar 2026.
|
|
17
29
|
def self.fiber_scheduler_v2?
|
|
30
|
+
# Some interface changes were back-ported incorrectly:
|
|
31
|
+
# https://github.com/ruby/ruby/pull/10778
|
|
32
|
+
# Specifically "Improvements to IO::Buffer read/write/pread/pwrite."
|
|
33
|
+
# Missing correct size calculation.
|
|
34
|
+
return false if RUBY_VERSION >= "3.2.5"
|
|
35
|
+
|
|
18
36
|
IO.const_defined?(:Buffer) and Fiber.respond_to?(:blocking) and IO::Buffer.instance_method(:read).arity == -1
|
|
19
37
|
end
|
|
20
38
|
|
|
39
|
+
# Updated inferfaces for read/write and IO::Buffer were introduced in Ruby 3.3, including pread/pwrite.
|
|
40
|
+
#
|
|
41
|
+
# To become the default 31 Mar 2026.
|
|
21
42
|
def self.fiber_scheduler_v3?
|
|
22
43
|
if fiber_scheduler_v2?
|
|
23
44
|
return true if RUBY_VERSION >= "3.3"
|
data/lib/io/event/timers.rb
CHANGED
|
@@ -3,46 +3,68 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2024, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
6
|
+
require_relative "priority_heap"
|
|
7
7
|
|
|
8
8
|
class IO
|
|
9
9
|
module Event
|
|
10
|
+
# An efficient sorted set of timers.
|
|
10
11
|
class Timers
|
|
12
|
+
# A handle to a scheduled timer.
|
|
11
13
|
class Handle
|
|
14
|
+
# Initialize the handle with the given time and block.
|
|
15
|
+
#
|
|
16
|
+
# @parameter time [Float] The time at which the block should be called.
|
|
17
|
+
# @parameter block [Proc] The block to call.
|
|
12
18
|
def initialize(time, block)
|
|
13
19
|
@time = time
|
|
14
20
|
@block = block
|
|
15
21
|
end
|
|
16
22
|
|
|
23
|
+
# @attribute [Float] The time at which the block should be called.
|
|
24
|
+
attr :time
|
|
25
|
+
|
|
26
|
+
# @attribute [Proc | Nil] The block to call when the timer fires.
|
|
27
|
+
attr :block
|
|
28
|
+
|
|
29
|
+
# Compare the handle with another handle.
|
|
30
|
+
#
|
|
31
|
+
# @parameter other [Handle] The other handle to compare with.
|
|
32
|
+
# @returns [Boolean] Whether the handle is less than the other handle.
|
|
17
33
|
def < other
|
|
18
34
|
@time < other.time
|
|
19
35
|
end
|
|
20
36
|
|
|
37
|
+
# Compare the handle with another handle.
|
|
38
|
+
#
|
|
39
|
+
# @parameter other [Handle] The other handle to compare with.
|
|
40
|
+
# @returns [Boolean] Whether the handle is greater than the other handle.
|
|
21
41
|
def > other
|
|
22
42
|
@time > other.time
|
|
23
43
|
end
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
attr :block
|
|
27
|
-
|
|
45
|
+
# Invoke the block.
|
|
28
46
|
def call(...)
|
|
29
47
|
@block.call(...)
|
|
30
48
|
end
|
|
31
49
|
|
|
50
|
+
# Cancel the timer.
|
|
32
51
|
def cancel!
|
|
33
52
|
@block = nil
|
|
34
53
|
end
|
|
35
54
|
|
|
55
|
+
# @returns [Boolean] Whether the timer has been cancelled.
|
|
36
56
|
def cancelled?
|
|
37
57
|
@block.nil?
|
|
38
58
|
end
|
|
39
59
|
end
|
|
40
60
|
|
|
61
|
+
# Initialize the timers.
|
|
41
62
|
def initialize
|
|
42
63
|
@heap = PriorityHeap.new
|
|
43
64
|
@scheduled = []
|
|
44
65
|
end
|
|
45
66
|
|
|
67
|
+
# @returns [Integer] The number of timers in the heap.
|
|
46
68
|
def size
|
|
47
69
|
flush!
|
|
48
70
|
|
|
@@ -50,7 +72,9 @@ class IO
|
|
|
50
72
|
end
|
|
51
73
|
|
|
52
74
|
# Schedule a block to be called at a specific time in the future.
|
|
75
|
+
#
|
|
53
76
|
# @parameter time [Float] The time at which the block should be called, relative to {#now}.
|
|
77
|
+
# @parameter block [Proc] The block to call.
|
|
54
78
|
def schedule(time, block)
|
|
55
79
|
handle = Handle.new(time, block)
|
|
56
80
|
|
|
@@ -60,11 +84,18 @@ class IO
|
|
|
60
84
|
end
|
|
61
85
|
|
|
62
86
|
# Schedule a block to be called after a specific time offset, relative to the current time as returned by {#now}.
|
|
87
|
+
#
|
|
63
88
|
# @parameter offset [#to_f] The time offset from the current time at which the block should be called.
|
|
89
|
+
# @yields {|now| ...} When the timer fires.
|
|
64
90
|
def after(offset, &block)
|
|
65
91
|
schedule(self.now + offset.to_f, block)
|
|
66
92
|
end
|
|
67
93
|
|
|
94
|
+
|
|
95
|
+
# Compute the time interval until the next timer fires.
|
|
96
|
+
#
|
|
97
|
+
# @parameter now [Float] The current time.
|
|
98
|
+
# @returns [Float | Nil] The time interval until the next timer fires, if any.
|
|
68
99
|
def wait_interval(now = self.now)
|
|
69
100
|
flush!
|
|
70
101
|
|
|
@@ -77,10 +108,14 @@ class IO
|
|
|
77
108
|
end
|
|
78
109
|
end
|
|
79
110
|
|
|
111
|
+
# @returns [Float] The current time.
|
|
80
112
|
def now
|
|
81
113
|
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
82
114
|
end
|
|
83
115
|
|
|
116
|
+
# Fire all timers that are ready to fire.
|
|
117
|
+
#
|
|
118
|
+
# @parameter now [Float] The current time.
|
|
84
119
|
def fire(now = self.now)
|
|
85
120
|
# Flush scheduled timers into the heap:
|
|
86
121
|
flush!
|
|
@@ -101,6 +136,9 @@ class IO
|
|
|
101
136
|
end
|
|
102
137
|
end
|
|
103
138
|
|
|
139
|
+
# Flush all scheduled timers into the heap.
|
|
140
|
+
#
|
|
141
|
+
# This is a small optimization which assumes that most timers (timeouts) will be cancelled.
|
|
104
142
|
protected def flush!
|
|
105
143
|
while handle = @scheduled.pop
|
|
106
144
|
@heap.push(handle) unless handle.cancelled?
|
data/lib/io/event/version.rb
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
|
+
# @namespace
|
|
6
7
|
class IO
|
|
8
|
+
# @namespace
|
|
7
9
|
module Event
|
|
8
|
-
VERSION = "1.
|
|
10
|
+
VERSION = "1.9.0"
|
|
9
11
|
end
|
|
10
12
|
end
|
data/lib/io/event.rb
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
|
|
10
|
-
begin
|
|
11
|
-
require 'IO_Event'
|
|
12
|
-
rescue LoadError => error
|
|
13
|
-
warn "Could not load native event selector: #{error}"
|
|
14
|
-
require_relative 'event/selector/nonblock'
|
|
15
|
-
end
|
|
6
|
+
require_relative "event/version"
|
|
7
|
+
require_relative "event/selector"
|
|
8
|
+
require_relative "event/timers"
|
|
9
|
+
require_relative "event/native"
|
data/license.md
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
3
|
Copyright, 2021, by Wander Hillen.
|
|
4
|
-
Copyright, 2021-
|
|
4
|
+
Copyright, 2021-2025, by Samuel Williams.
|
|
5
5
|
Copyright, 2021, by Delton Ding.
|
|
6
6
|
Copyright, 2021-2024, by Benoit Daloze.
|
|
7
7
|
Copyright, 2022, by Alex Matchneer.
|
|
8
8
|
Copyright, 2022, by Bruno Sutic.
|
|
9
9
|
Copyright, 2023, by Math Ieu.
|
|
10
10
|
Copyright, 2024, by Pavel Rosický.
|
|
11
|
+
Copyright, 2024, by Anthony Ross.
|
|
12
|
+
Copyright, 2024, by Shizuo Fujita.
|
|
13
|
+
Copyright, 2024, by Jean Boussier.
|
|
11
14
|
|
|
12
15
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
16
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
|
@@ -10,7 +10,25 @@ The initial proof-of-concept [Async](https://github.com/socketry/async) was buil
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
Please see the [project documentation](https://socketry.github.io/io-event/).
|
|
13
|
+
Please see the [project documentation](https://socketry.github.io/io-event/) for more details.
|
|
14
|
+
|
|
15
|
+
- [Getting Started](https://socketry.github.io/io-event/guides/getting-started/index) - This guide explains how to use `io-event` for non-blocking IO.
|
|
16
|
+
|
|
17
|
+
## Releases
|
|
18
|
+
|
|
19
|
+
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
|
20
|
+
|
|
21
|
+
### v1.9.0
|
|
22
|
+
|
|
23
|
+
- [Improved `IO::Event::Profiler` for detecting stalls.](https://socketry.github.io/io-event/releases/index#improved-io::event::profiler-for-detecting-stalls.)
|
|
24
|
+
|
|
25
|
+
### v1.8.0
|
|
26
|
+
|
|
27
|
+
- [Detecting fibers that are stalling the event loop.](https://socketry.github.io/io-event/releases/index#detecting-fibers-that-are-stalling-the-event-loop.)
|
|
28
|
+
|
|
29
|
+
### v1.7.5
|
|
30
|
+
|
|
31
|
+
- Fix `process_wait` race condition on EPoll that could cause a hang.
|
|
14
32
|
|
|
15
33
|
## Contributing
|
|
16
34
|
|
|
@@ -24,8 +42,8 @@ We welcome contributions to this project.
|
|
|
24
42
|
|
|
25
43
|
### Developer Certificate of Origin
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
|
28
46
|
|
|
29
|
-
###
|
|
47
|
+
### Community Guidelines
|
|
30
48
|
|
|
31
|
-
This project is
|
|
49
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data/releases.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
## v1.9.0
|
|
4
|
+
|
|
5
|
+
### Improved `IO::Event::Profiler` for detecting stalls.
|
|
6
|
+
|
|
7
|
+
A new `IO::Event::Profiler` class has been added to help detect stalls in the event loop. The previous approach was insufficient to detect all possible stalls. This new approach uses the `RUBY_EVENT_FIBER_SWITCH` event to track context switching by the scheduler, and can detect stalls no matter how they occur.
|
|
8
|
+
|
|
9
|
+
``` ruby
|
|
10
|
+
profiler = IO::Event::Profiler.new
|
|
11
|
+
|
|
12
|
+
profiler.start
|
|
13
|
+
|
|
14
|
+
Fiber.new do
|
|
15
|
+
sleep 1.0
|
|
16
|
+
end.transfer
|
|
17
|
+
|
|
18
|
+
profiler.stop
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
A default profiler is exposed using `IO::Event::Profiler.default` which is controlled by the following environment variables:
|
|
22
|
+
|
|
23
|
+
- `IO_EVENT_PROFILER=true` - Enable the profiler, otherwise `IO::Event::Profiler.default` will return `nil`.
|
|
24
|
+
- `IO_EVENT_PROFILER_LOG_THRESHOLD` - Specify the threshold in seconds for logging a stall. Defaults to `0.01`.
|
|
25
|
+
- `IO_EVENT_PROFILER_TRACK_CALLS` - Track the method call for each event, in order to log specifically which method is causing the stall. Defaults to `true`.
|
|
26
|
+
|
|
27
|
+
The previous environment variables `IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD` and `IO_EVENT_SELECTOR_STALL_LOG` no longer have any effect.
|
|
28
|
+
|
|
29
|
+
## v1.8.0
|
|
30
|
+
|
|
31
|
+
### Detecting fibers that are stalling the event loop.
|
|
32
|
+
|
|
33
|
+
A new (experimental) feature for detecting fiber stalls has been added. This feature is disabled by default and can be enabled by setting the `IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD` to `true` or a floating point number representing the threshold in seconds.
|
|
34
|
+
|
|
35
|
+
When enabled, the event loop will measure and profile user code when resuming a fiber. If the fiber takes too long to return back to the event loop, the event loop will log a warning message with a profile of the fiber's execution.
|
|
36
|
+
|
|
37
|
+
> cat test.rb
|
|
38
|
+
#!/usr/bin/env ruby
|
|
39
|
+
|
|
40
|
+
require_relative "lib/async"
|
|
41
|
+
|
|
42
|
+
Async do
|
|
43
|
+
Fiber.blocking do
|
|
44
|
+
sleep 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
> IO_EVENT_SELECTOR_STALL_LOG_THRESHOLD=true bundle exec ./test.rb
|
|
49
|
+
Fiber stalled for 1.003 seconds
|
|
50
|
+
/home/samuel/Developer/socketry/async/test.rb:6 in '#<Class:Fiber>#blocking' (1s)
|
|
51
|
+
/home/samuel/Developer/socketry/async/test.rb:7 in 'Kernel#sleep' (1s)
|
|
52
|
+
|
|
53
|
+
There is a performance overhead to this feature, so it is recommended to only enable it when debugging performance issues.
|
|
54
|
+
|
|
55
|
+
## v1.7.5
|
|
56
|
+
|
|
57
|
+
- Fix `process_wait` race condition on EPoll that could cause a hang.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: io-event
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
8
8
|
- Math Ieu
|
|
9
9
|
- Wander Hillen
|
|
10
|
+
- Jean Boussier
|
|
10
11
|
- Benoit Daloze
|
|
11
12
|
- Bruno Sutic
|
|
12
13
|
- Alex Matchneer
|
|
14
|
+
- Anthony Ross
|
|
13
15
|
- Delton Ding
|
|
14
16
|
- Pavel Rosický
|
|
15
|
-
|
|
17
|
+
- Shizuo Fujita
|
|
16
18
|
bindir: bin
|
|
17
19
|
cert_chain:
|
|
18
20
|
- |
|
|
@@ -44,10 +46,8 @@ cert_chain:
|
|
|
44
46
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
|
45
47
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
|
46
48
|
-----END CERTIFICATE-----
|
|
47
|
-
date:
|
|
49
|
+
date: 2025-02-10 00:00:00.000000000 Z
|
|
48
50
|
dependencies: []
|
|
49
|
-
description:
|
|
50
|
-
email:
|
|
51
51
|
executables: []
|
|
52
52
|
extensions:
|
|
53
53
|
- ext/extconf.rb
|
|
@@ -55,25 +55,33 @@ extra_rdoc_files: []
|
|
|
55
55
|
files:
|
|
56
56
|
- design.md
|
|
57
57
|
- ext/extconf.rb
|
|
58
|
+
- ext/io/event/array.h
|
|
58
59
|
- ext/io/event/event.c
|
|
59
60
|
- ext/io/event/event.h
|
|
61
|
+
- ext/io/event/fiber.c
|
|
62
|
+
- ext/io/event/fiber.h
|
|
60
63
|
- ext/io/event/interrupt.c
|
|
61
64
|
- ext/io/event/interrupt.h
|
|
62
|
-
- ext/io/event/
|
|
65
|
+
- ext/io/event/list.h
|
|
66
|
+
- ext/io/event/profiler.c
|
|
67
|
+
- ext/io/event/profiler.h
|
|
63
68
|
- ext/io/event/selector/epoll.c
|
|
64
69
|
- ext/io/event/selector/epoll.h
|
|
65
70
|
- ext/io/event/selector/kqueue.c
|
|
66
71
|
- ext/io/event/selector/kqueue.h
|
|
67
|
-
- ext/io/event/selector/list.h
|
|
68
72
|
- ext/io/event/selector/pidfd.c
|
|
69
73
|
- ext/io/event/selector/selector.c
|
|
70
74
|
- ext/io/event/selector/selector.h
|
|
71
75
|
- ext/io/event/selector/uring.c
|
|
72
76
|
- ext/io/event/selector/uring.h
|
|
77
|
+
- ext/io/event/time.c
|
|
78
|
+
- ext/io/event/time.h
|
|
73
79
|
- lib/io/event.rb
|
|
74
80
|
- lib/io/event/debug/selector.rb
|
|
75
81
|
- lib/io/event/interrupt.rb
|
|
82
|
+
- lib/io/event/native.rb
|
|
76
83
|
- lib/io/event/priority_heap.rb
|
|
84
|
+
- lib/io/event/profiler.rb
|
|
77
85
|
- lib/io/event/selector.rb
|
|
78
86
|
- lib/io/event/selector/nonblock.rb
|
|
79
87
|
- lib/io/event/selector/select.rb
|
|
@@ -82,11 +90,13 @@ files:
|
|
|
82
90
|
- lib/io/event/version.rb
|
|
83
91
|
- license.md
|
|
84
92
|
- readme.md
|
|
93
|
+
- releases.md
|
|
85
94
|
homepage: https://github.com/socketry/io-event
|
|
86
95
|
licenses:
|
|
87
96
|
- MIT
|
|
88
|
-
metadata:
|
|
89
|
-
|
|
97
|
+
metadata:
|
|
98
|
+
documentation_uri: https://socketry.github.io/io-event/
|
|
99
|
+
source_code_uri: https://github.com/socketry/io-event.git
|
|
90
100
|
rdoc_options: []
|
|
91
101
|
require_paths:
|
|
92
102
|
- lib
|
|
@@ -101,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
101
111
|
- !ruby/object:Gem::Version
|
|
102
112
|
version: '0'
|
|
103
113
|
requirements: []
|
|
104
|
-
rubygems_version: 3.
|
|
105
|
-
signing_key:
|
|
114
|
+
rubygems_version: 3.6.2
|
|
106
115
|
specification_version: 4
|
|
107
116
|
summary: An event loop.
|
|
108
117
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|