class AttemptTimeout
Custom timeout implementation that’s more reliable than Ruby’s Timeout module
Public Class Methods
Source
# File lib/attempt.rb, line 109 def self.fiber_compatible_block?(&block) # For now, assume most blocks are not naturally fiber-cooperative # In practice, you'd want more sophisticated detection false end
Simple heuristic to determine if a block is likely to be fiber-compatible (This is a basic implementation - in practice, this is hard to determine)
Source
# File lib/attempt.rb, line 53 def self.fiber_only_timeout(seconds, &block) fiber = Fiber.new(&block) start_time = Time.now loop do elapsed = Time.now - start_time if elapsed > seconds raise Error, "execution expired after #{seconds} seconds" end begin result = fiber.resume return result unless fiber.alive? rescue FiberError # Fiber is dead, which means it completed break end # Small sleep to prevent busy waiting and allow other operations sleep 0.001 end end
Pure fiber-based timeout for cooperative code
Source
# File lib/attempt.rb, line 77 def self.fiber_thread_hybrid_timeout(seconds, &block) result = nil exception = nil thread = Thread.new do fiber = Fiber.new do begin result = yield rescue => e exception = e end end # Resume the fiber until completion while fiber.alive? fiber.resume Thread.pass # Allow other threads to run end end if thread.join(seconds) raise exception if exception result else thread.kill thread.join(0.1) raise Error, "execution expired after #{seconds} seconds" end end
Hybrid approach: fiber in a thread with timeout
Source
# File lib/attempt.rb, line 40 def self.fiber_timeout(seconds, &block) return yield if seconds.nil? || seconds <= 0 # For blocks that don't naturally yield, we need a different approach # We'll use a hybrid fiber + thread approach for better compatibility if fiber_compatible_block?(&block) fiber_only_timeout(seconds, &block) else fiber_thread_hybrid_timeout(seconds, &block) end end
Alternative: Fiber-based timeout (lightweight, cooperative) Note: This only works for code that yields control back to the main thread
Source
# File lib/attempt.rb, line 12 def self.timeout(seconds, &block) return yield if seconds.nil? || seconds <= 0 result = nil exception = nil thread = Thread.new do begin result = yield rescue => e exception = e end end if thread.join(seconds) # Thread completed within timeout raise exception if exception result else # Thread timed out thread.kill # More graceful than Thread#raise thread.join # Wait for cleanup raise Error, "execution expired after #{seconds} seconds" end end
More reliable timeout using Thread + sleep instead of Timeout.timeout This approach is safer as it doesn’t use Thread#raise