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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/ext/extconf.rb +26 -13
  4. data/ext/io/event/{selector/array.h → array.h} +74 -18
  5. data/ext/io/event/event.c +9 -26
  6. data/ext/io/event/event.h +2 -19
  7. data/ext/io/event/fiber.c +63 -0
  8. data/ext/io/event/fiber.h +23 -0
  9. data/ext/io/event/interrupt.c +19 -27
  10. data/ext/io/event/interrupt.h +2 -19
  11. data/ext/io/event/{selector/list.h → list.h} +20 -2
  12. data/ext/io/event/profiler.c +505 -0
  13. data/ext/io/event/profiler.h +8 -0
  14. data/ext/io/event/selector/epoll.c +41 -43
  15. data/ext/io/event/selector/epoll.h +2 -19
  16. data/ext/io/event/selector/kqueue.c +34 -41
  17. data/ext/io/event/selector/kqueue.h +2 -19
  18. data/ext/io/event/selector/pidfd.c +2 -19
  19. data/ext/io/event/selector/selector.c +65 -102
  20. data/ext/io/event/selector/selector.h +51 -46
  21. data/ext/io/event/selector/uring.c +130 -47
  22. data/ext/io/event/selector/uring.h +2 -19
  23. data/ext/io/event/time.c +35 -0
  24. data/ext/io/event/time.h +17 -0
  25. data/lib/io/event/debug/selector.rb +44 -4
  26. data/lib/io/event/interrupt.rb +2 -2
  27. data/lib/io/event/native.rb +11 -0
  28. data/lib/io/event/priority_heap.rb +13 -14
  29. data/lib/io/event/profiler.rb +18 -0
  30. data/lib/io/event/selector/nonblock.rb +6 -2
  31. data/lib/io/event/selector/select.rb +25 -5
  32. data/lib/io/event/selector.rb +15 -5
  33. data/lib/io/event/support.rb +23 -2
  34. data/lib/io/event/timers.rb +42 -4
  35. data/lib/io/event/version.rb +4 -2
  36. data/lib/io/event.rb +5 -11
  37. data/license.md +4 -1
  38. data/readme.md +22 -4
  39. data/releases.md +57 -0
  40. data.tar.gz.sig +0 -0
  41. metadata +20 -11
  42. 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
- # Returns the earliest timer or nil if the heap is empty.
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
- # Returns the number of elements in the heap
26
+ # @returns [Integer] the number of elements in the heap.
26
27
  def size
27
28
  @contents.size
28
29
  end
29
30
 
30
- # Returns the earliest timer if the heap is non-empty and removes it from the heap.
31
- # Returns nil if the heap is empty. (and doesn't change the heap in that case)
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
- # Inserts a new timer into the heap, then rearranges elements until the heap invariant is true again.
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
- # notice we skip index 0 on purpose, because it has no parent
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
- # if the node has a smaller value than its parent, swap these nodes
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-2023, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
- require 'io/nonblock'
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 '../interrupt'
8
- require_relative '../support'
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
- # If the event loop is currently sleeping, wake it up.
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
- def again?(errno)
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)
@@ -3,14 +3,19 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
- require_relative 'selector/select'
7
- require_relative 'debug/selector'
8
- require_relative 'support'
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['IO_EVENT_SELECTOR']&.to_sym
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['IO_EVENT_DEBUG_SELECTOR']
41
+ if debug = env["IO_EVENT_DEBUG_SELECTOR"]
32
42
  selector = Debug::Selector.wrap(selector, env)
33
43
  end
34
44
 
@@ -1,23 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2023, by Samuel Williams.
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) and !Fiber.respond_to?(:blocking)
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"
@@ -3,46 +3,68 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2024, by Samuel Williams.
5
5
 
6
- require_relative 'priority_heap'
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
- attr :time
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?
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
4
+ # Copyright, 2021-2025, by Samuel Williams.
5
5
 
6
+ # @namespace
6
7
  class IO
8
+ # @namespace
7
9
  module Event
8
- VERSION = "1.6.5"
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-2024, by Samuel Williams.
4
+ # Copyright, 2021-2025, by Samuel Williams.
5
5
 
6
- require_relative 'event/version'
7
- require_relative 'event/selector'
8
- require_relative 'event/timers'
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-2024, by Samuel Williams.
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
- This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
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
- ### Contributor Covenant
47
+ ### Community Guidelines
30
48
 
31
- This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
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.6.5
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
- autorequire:
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: 2024-06-23 00:00:00.000000000 Z
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/selector/array.h
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
- post_install_message:
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.5.11
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