Releases
v1.16.0
- Use
eventfdforURingcross-thread wakeup, and enableIORING_SETUP_SINGLE_ISSUER,IORING_SETUP_DEFER_TASKRUN, andIORING_SETUP_TASKRUN_FLAG. The waking thread now signals viaeventfdrather than submitting aNOPSQE, which unlocks the single-issuer optimisation, defers task work to the application thread, and letsselect()skip theio_uring_get_events()syscall when no task work is pending. - Add support for the
io_closefiber-scheduler hook (Ruby 4.0+). TheURingselector performs the close asynchronously via the ring; theDebug::SelectorandTestSchedulerwrappers forward to the underlying selector when supported. - Improve
WorkerPoolGC compaction support and add proper write barriers, fixing potential use-after-free under compacting GC. - Keep blocked scheduler fibers alive during GC by registering them as roots in
TestScheduler#block, preventing premature collection and the resulting use-after-free crash on resume. - Use Ruby's
xmalloc/xcalloc/xrealloc2/xfreefor all internal selector allocations (the per-fiber ready-queue entries inIO_Event_Selector_ready_push, and both the backing array and per-element allocations inIO_Event_Array). Previously a rawmallocpaired with a debug-build-onlyassert(...)would silently dereferenceNULLand crash in release builds under memory pressure; the Ruby allocators trigger a GC sweep on pressure and raiseNoMemoryError/RangeErroron real failure, so the-1return-code paths throughIO_Event_Array_initialize/_resize/_lookupand their callers inepoll.c/kqueue.c/uring.care removed in favour of straight exception propagation. - Correctly handle short
io_uring_submit()results in theURingselector.io_uring_submit()returns the number of SQEs actually accepted by the kernel and can be short (SQE prep errors,ENOMEM, transientEAGAIN); the old accounting resetpending = 0on any success and silently lost track of unsubmitted SQEs. - Enable
IORING_SETUP_SUBMIT_ALL(kernel 5.18+) on theURingselector so the kernel keeps processing the rest of an SQE batch past individual errors, reducing the frequency of short submits in practice.
v1.15.1
- Simplify closed-IO handling in the
Selectselector: rely on Ruby 4'srb_thread_io_close_interruptto wake fibers waiting on a descriptor that's been closed, removing a custom error-recovery path that could mis-attributeIOError/Errno::EBADFto the wrong waiter.
v1.15.0
- Add bounds checks, in the unlikely event of a user providing an invalid offset that exceeds the buffer size. This prevents potential memory corruption and ensures safe operation when using buffered IO methods.
v1.14.4
- Allow
epoll_pwait2to be disabled via--disable-epoll_pwait2.
v1.14.3
- Fix several implementation bugs that could cause deadlocks on blocking writes.
v1.14.0
Enhanced IO::Event::PriorityHeap with deletion and bulk insertion methods
The class IO::Event::PriorityHeap now supports efficient element removal and bulk insertion:
delete(element): Remove a specific element from the heap in O(n) timedelete_if(&block): Remove elements matching a condition with O(n) amortized bulk deletionconcat(elements): Add multiple elements efficiently in O(n) time
heap = IO::Event::PriorityHeap.new
# Efficient bulk insertion - O(n) instead of O(n log n)
heap.concat([5, 2, 8, 1, 9, 3])
# Remove specific element
removed = heap.delete(5) # Returns 5, heap maintains order
# Bulk removal with condition
count = heap.delete_if{|x| x.even?} # Removes 2, 8 efficiently
The delete_if and concat methods are particularly efficient for bulk operations, using bottom-up heapification to maintain the heap property in O(n) time. This provides significant performance improvements:
- Bulk insertion: O(n log n) → O(n) for adding multiple elements
- Bulk deletion: O(k×n) → O(n) for removing k elements
Both methods maintain the heap invariant and include comprehensive test coverage with edge case validation.
v1.11.2
- Fix Windows build.
v1.11.1
- Fix
read_nonblockwhen using theURingselector, which was not handling zero-length reads correctly. This allows reading available data without blocking.
v1.11.0
Introduce IO::Event::WorkerPool for off-loading blocking operations.
The IO::Event::WorkerPool provides a mechanism for executing blocking operations on separate OS threads while properly integrating with Ruby's fiber scheduler and GVL (Global VM Lock) management. This enables true parallelism for CPU-intensive or blocking operations that would otherwise block the event loop.
# Fiber scheduler integration via blocking_operation_wait hook
class MyScheduler
def initialize
@worker_pool = IO::Event::WorkerPool.new
end
def blocking_operation_wait(operation)
@worker_pool.call(operation)
end
end
# Usage with automatic offloading
Fiber.set_scheduler(MyScheduler.new)
# Automatically offload `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` to a background thread:
result = some_blocking_operation()
The implementation uses one or more background threads and a list of pending blocking operations. Those operations either execute through to completion or may be cancelled, which executes the "unblock function" provided to rb_nogvl.
v1.10.2
- Improved consistency of handling closed IO when invoking
#select.
v1.10.0
IO::Event::Profileris moved to dedicated gem: fiber-profiler.- Perform runtime checks for native selectors to ensure they are supported in the current environment. While compile-time checks determine availability, restrictions like seccomp and SELinux may still prevent them from working.
v1.9.0
- Improved
IO::Event::Profilerfor detecting stalls.
v1.8.0
- Detecting fibers that are stalling the event loop.
v1.7.5
- Fix
process_waitrace condition on EPoll that could cause a hang.