io-event 1.7.4 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/ext/io/event/selector/epoll.c +1 -1
- data/lib/io/event/debug/selector.rb +41 -1
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/selector/nonblock.rb +4 -0
- data/lib/io/event/selector/select.rb +19 -3
- data/lib/io/event/selector.rb +10 -0
- data/lib/io/event/support.rb +12 -0
- data/lib/io/event/timers.rb +41 -3
- data/lib/io/event/version.rb +3 -1
- data/readme.md +11 -1
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ab271a1de3eb0f5d21b0b7c92cafcc226e5ca218e7b87ebaa80f93a54fd7b945
         | 
| 4 | 
            +
              data.tar.gz: fa6227d0b4218b277903fb1c6f1889e51bcfb7e8d8f83086edc30a336a065eff
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d5afc83ef6364791d86b14bdf130d68fda3a7a1c720349dce6bb2140a2ee9f69015e542dbf5c724a0daa338e5f13c87f1dee566e8a36a1b0847ce5e86e1fa92b
         | 
| 7 | 
            +
              data.tar.gz: 7d09237e5141123bf251fe686e25fca7d8391a7c25fe331e8882d7e43bf87ff9c5e89bc5c5f90d0731705b923eade10c4667344ad06d04a08544bb9a944baa28
         | 
    
        checksums.yaml.gz.sig
    CHANGED
    
    | Binary file | 
| @@ -519,7 +519,7 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid, | |
| 519 519 |  | 
| 520 520 | 
             
            	RB_OBJ_WRITTEN(self, Qundef, fiber);
         | 
| 521 521 |  | 
| 522 | 
            -
            	int result = IO_Event_Selector_EPoll_Waiting_register(selector,  | 
| 522 | 
            +
            	int result = IO_Event_Selector_EPoll_Waiting_register(selector, _pid, descriptor, &waiting);
         | 
| 523 523 |  | 
| 524 524 | 
             
            	if (result == -1) {
         | 
| 525 525 | 
             
            		close(descriptor);
         | 
| @@ -6,9 +6,16 @@ | |
| 6 6 | 
             
            require_relative "../support"
         | 
| 7 7 |  | 
| 8 8 | 
             
            module IO::Event
         | 
| 9 | 
            +
            	# @namespace
         | 
| 9 10 | 
             
            	module Debug
         | 
| 10 11 | 
             
            		# Enforces the selector interface and delegates operations to a wrapped selector instance.
         | 
| 12 | 
            +
            		#
         | 
| 13 | 
            +
            		# You can enable this in the default selector by setting the `IO_EVENT_DEBUG_SELECTOR` environment variable. In addition, you can log all selector operations to a file by setting the `IO_EVENT_DEBUG_SELECTOR_LOG` environment variable. This is useful for debugging and understanding the behavior of the event loop.
         | 
| 11 14 | 
             
            		class Selector
         | 
| 15 | 
            +
            			# Wrap the given selector with debugging.
         | 
| 16 | 
            +
            			#
         | 
| 17 | 
            +
            			# @parameter selector [Selector] The selector to wrap.
         | 
| 18 | 
            +
            			# @parameter env [Hash] The environment to read configuration from.
         | 
| 12 19 | 
             
            			def self.wrap(selector, env = ENV)
         | 
| 13 20 | 
             
            				log = nil
         | 
| 14 21 |  | 
| @@ -19,6 +26,10 @@ module IO::Event | |
| 19 26 | 
             
            				return self.new(selector, log: log)
         | 
| 20 27 | 
             
            			end
         | 
| 21 28 |  | 
| 29 | 
            +
            			# Initialize the debug selector with the given selector and optional log.
         | 
| 30 | 
            +
            			#
         | 
| 31 | 
            +
            			# @parameter selector [Selector] The selector to wrap.
         | 
| 32 | 
            +
            			# @parameter log [IO] The log to write debug messages to.
         | 
| 22 33 | 
             
            			def initialize(selector, log: nil)
         | 
| 23 34 | 
             
            				@selector = selector
         | 
| 24 35 |  | 
| @@ -33,14 +44,23 @@ module IO::Event | |
| 33 44 | 
             
            				@log = log
         | 
| 34 45 | 
             
            			end
         | 
| 35 46 |  | 
| 47 | 
            +
            			# The idle duration of the underlying selector.
         | 
| 48 | 
            +
            			#
         | 
| 49 | 
            +
            			# @returns [Numeric] The idle duration.
         | 
| 36 50 | 
             
            			def idle_duration
         | 
| 37 51 | 
             
            				@selector.idle_duration
         | 
| 38 52 | 
             
            			end
         | 
| 39 53 |  | 
| 54 | 
            +
            			# The current time.
         | 
| 55 | 
            +
            			#
         | 
| 56 | 
            +
            			# @returns [Numeric] The current time.
         | 
| 40 57 | 
             
            			def now
         | 
| 41 58 | 
             
            				Process.clock_gettime(Process::CLOCK_MONOTONIC)
         | 
| 42 59 | 
             
            			end
         | 
| 43 60 |  | 
| 61 | 
            +
            			# Log the given message.
         | 
| 62 | 
            +
            			#
         | 
| 63 | 
            +
            			# @asynchronous Will block the calling fiber and the entire event loop.
         | 
| 44 64 | 
             
            			def log(message)
         | 
| 45 65 | 
             
            				return unless @log
         | 
| 46 66 |  | 
| @@ -49,10 +69,12 @@ module IO::Event | |
| 49 69 | 
             
            				end
         | 
| 50 70 | 
             
            			end
         | 
| 51 71 |  | 
| 72 | 
            +
            			# Wakeup the the selector.
         | 
| 52 73 | 
             
            			def wakeup
         | 
| 53 74 | 
             
            				@selector.wakeup
         | 
| 54 75 | 
             
            			end
         | 
| 55 76 |  | 
| 77 | 
            +
            			# Close the selector.
         | 
| 56 78 | 
             
            			def close
         | 
| 57 79 | 
             
            				log("Closing selector")
         | 
| 58 80 |  | 
| @@ -64,60 +86,78 @@ module IO::Event | |
| 64 86 | 
             
            				@selector = nil
         | 
| 65 87 | 
             
            			end
         | 
| 66 88 |  | 
| 67 | 
            -
            			# Transfer from the calling fiber to the  | 
| 89 | 
            +
            			# Transfer from the calling fiber to the selector.
         | 
| 68 90 | 
             
            			def transfer
         | 
| 69 91 | 
             
            				log("Transfering to event loop")
         | 
| 70 92 | 
             
            				@selector.transfer
         | 
| 71 93 | 
             
            			end
         | 
| 72 94 |  | 
| 95 | 
            +
            			# Resume the given fiber with the given arguments.
         | 
| 73 96 | 
             
            			def resume(*arguments)
         | 
| 74 97 | 
             
            				log("Resuming fiber with #{arguments.inspect}")
         | 
| 75 98 | 
             
            				@selector.resume(*arguments)
         | 
| 76 99 | 
             
            			end
         | 
| 77 100 |  | 
| 101 | 
            +
            			# Yield to the selector.
         | 
| 78 102 | 
             
            			def yield
         | 
| 79 103 | 
             
            				log("Yielding to event loop")
         | 
| 80 104 | 
             
            				@selector.yield
         | 
| 81 105 | 
             
            			end
         | 
| 82 106 |  | 
| 107 | 
            +
            			# Push the given fiber to the selector ready list, such that it will be resumed on the next call to {select}.
         | 
| 108 | 
            +
            			#
         | 
| 109 | 
            +
            			# @parameter fiber [Fiber] The fiber that is ready.
         | 
| 83 110 | 
             
            			def push(fiber)
         | 
| 84 111 | 
             
            				log("Pushing fiber #{fiber.inspect} to ready list")
         | 
| 85 112 | 
             
            				@selector.push(fiber)
         | 
| 86 113 | 
             
            			end
         | 
| 87 114 |  | 
| 115 | 
            +
            			# Raise the given exception on the given fiber.
         | 
| 116 | 
            +
            			#
         | 
| 117 | 
            +
            			# @parameter fiber [Fiber] The fiber to raise the exception on.
         | 
| 118 | 
            +
            			# @parameter arguments [Array] The arguments to use when raising the exception.
         | 
| 88 119 | 
             
            			def raise(fiber, *arguments)
         | 
| 89 120 | 
             
            				log("Raising exception on fiber #{fiber.inspect} with #{arguments.inspect}")
         | 
| 90 121 | 
             
            				@selector.raise(fiber, *arguments)
         | 
| 91 122 | 
             
            			end
         | 
| 92 123 |  | 
| 124 | 
            +
            			# Check if the selector is ready.
         | 
| 125 | 
            +
            			#
         | 
| 126 | 
            +
            			# @returns [Boolean] Whether the selector is ready.
         | 
| 93 127 | 
             
            			def ready?
         | 
| 94 128 | 
             
            				@selector.ready?
         | 
| 95 129 | 
             
            			end
         | 
| 96 130 |  | 
| 131 | 
            +
            			# Wait for the given process, forwarded to the underlying selector.
         | 
| 97 132 | 
             
            			def process_wait(*arguments)
         | 
| 98 133 | 
             
            				log("Waiting for process with #{arguments.inspect}")
         | 
| 99 134 | 
             
            				@selector.process_wait(*arguments)
         | 
| 100 135 | 
             
            			end
         | 
| 101 136 |  | 
| 137 | 
            +
            			# Wait for the given IO, forwarded to the underlying selector.
         | 
| 102 138 | 
             
            			def io_wait(fiber, io, events)
         | 
| 103 139 | 
             
            				log("Waiting for IO #{io.inspect} for events #{events.inspect}")
         | 
| 104 140 | 
             
            				@selector.io_wait(fiber, io, events)
         | 
| 105 141 | 
             
            			end
         | 
| 106 142 |  | 
| 143 | 
            +
            			# Read from the given IO, forwarded to the underlying selector.
         | 
| 107 144 | 
             
            			def io_read(fiber, io, buffer, length, offset = 0)
         | 
| 108 145 | 
             
            				log("Reading from IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
         | 
| 109 146 | 
             
            				@selector.io_read(fiber, io, buffer, length, offset)
         | 
| 110 147 | 
             
            			end
         | 
| 111 148 |  | 
| 149 | 
            +
            			# Write to the given IO, forwarded to the underlying selector.
         | 
| 112 150 | 
             
            			def io_write(fiber, io, buffer, length, offset = 0)
         | 
| 113 151 | 
             
            				log("Writing to IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
         | 
| 114 152 | 
             
            				@selector.io_write(fiber, io, buffer, length, offset)
         | 
| 115 153 | 
             
            			end
         | 
| 116 154 |  | 
| 155 | 
            +
            			# Forward the given method to the underlying selector.
         | 
| 117 156 | 
             
            			def respond_to?(name, include_private = false)
         | 
| 118 157 | 
             
            				@selector.respond_to?(name, include_private)
         | 
| 119 158 | 
             
            			end
         | 
| 120 159 |  | 
| 160 | 
            +
            			# Select for the given duration, forwarded to the underlying selector.
         | 
| 121 161 | 
             
            			def select(duration = nil)
         | 
| 122 162 | 
             
            				log("Selecting for #{duration.inspect}")
         | 
| 123 163 | 
             
            				unless Fiber.current == @selector.loop
         | 
| @@ -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 |  | 
| @@ -7,6 +7,10 @@ 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
         | 
| @@ -9,7 +9,9 @@ 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,7 +176,8 @@ 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 |  | 
    
        data/lib/io/event/selector.rb
    CHANGED
    
    | @@ -8,7 +8,12 @@ require_relative "debug/selector" | |
| 8 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 18 | 
             
            			if name = env["IO_EVENT_SELECTOR"]&.to_sym
         | 
| 14 19 | 
             
            				return const_get(name)
         | 
| @@ -25,6 +30,11 @@ 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 |  | 
    
        data/lib/io/event/support.rb
    CHANGED
    
    | @@ -5,16 +5,26 @@ | |
| 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 | 
            +
            			#
         | 
| 13 21 | 
             
            			# To be removed on 31 Mar 2025.
         | 
| 14 22 | 
             
            			def self.fiber_scheduler_v1?
         | 
| 15 23 | 
             
            				IO.const_defined?(:Buffer)
         | 
| 16 24 | 
             
            			end
         | 
| 17 25 |  | 
| 26 | 
            +
            			# More advanced read/write methods and blocking controls were introduced in Ruby 3.2.
         | 
| 27 | 
            +
            			#
         | 
| 18 28 | 
             
            			# To be removed on 31 Mar 2026.
         | 
| 19 29 | 
             
            			def self.fiber_scheduler_v2?
         | 
| 20 30 | 
             
            				# Some interface changes were back-ported incorrectly:
         | 
| @@ -26,6 +36,8 @@ class IO | |
| 26 36 | 
             
            				IO.const_defined?(:Buffer) and Fiber.respond_to?(:blocking) and IO::Buffer.instance_method(:read).arity == -1
         | 
| 27 37 | 
             
            			end
         | 
| 28 38 |  | 
| 39 | 
            +
            			# Updated inferfaces for read/write and IO::Buffer were introduced in Ruby 3.3, including pread/pwrite.
         | 
| 40 | 
            +
            			#
         | 
| 29 41 | 
             
            			# To become the default 31 Mar 2026.
         | 
| 30 42 | 
             
            			def self.fiber_scheduler_v3?
         | 
| 31 43 | 
             
            				if fiber_scheduler_v2?
         | 
    
        data/lib/io/event/timers.rb
    CHANGED
    
    | @@ -7,42 +7,64 @@ 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
    
    
    
        data/readme.md
    CHANGED
    
    | @@ -10,7 +10,17 @@ 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.7.5
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              - Fix `process_wait` race condition on EPoll that could cause a hang.
         | 
| 14 24 |  | 
| 15 25 | 
             
            ## Contributing
         | 
| 16 26 |  | 
    
        data/releases.md
    ADDED
    
    
    
        data.tar.gz.sig
    CHANGED
    
    | Binary file | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: io-event
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.7. | 
| 4 | 
            +
              version: 1.7.5
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Samuel Williams
         | 
| @@ -45,7 +45,7 @@ cert_chain: | |
| 45 45 | 
             
              Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
         | 
| 46 46 | 
             
              voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
         | 
| 47 47 | 
             
              -----END CERTIFICATE-----
         | 
| 48 | 
            -
            date: 2024- | 
| 48 | 
            +
            date: 2024-12-15 00:00:00.000000000 Z
         | 
| 49 49 | 
             
            dependencies: []
         | 
| 50 50 | 
             
            description:
         | 
| 51 51 | 
             
            email:
         | 
| @@ -83,6 +83,7 @@ files: | |
| 83 83 | 
             
            - lib/io/event/version.rb
         | 
| 84 84 | 
             
            - license.md
         | 
| 85 85 | 
             
            - readme.md
         | 
| 86 | 
            +
            - releases.md
         | 
| 86 87 | 
             
            homepage: https://github.com/socketry/io-event
         | 
| 87 88 | 
             
            licenses:
         | 
| 88 89 | 
             
            - MIT
         | 
    
        metadata.gz.sig
    CHANGED
    
    | Binary file |