Low commit activity in last 3 years
A cooperative timeout library providing deadline and timeout patterns that avoid Thread.raise, with nested deadline support, grace periods, callbacks, and explicit cancellation checks.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-timeout_kit

Tests Gem Version Last updated

Safe timeout patterns without Thread.raise

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-timeout_kit"

Or install directly:

gem install philiprehberger-timeout_kit

Usage

require "philiprehberger/timeout_kit"

Philiprehberger::TimeoutKit.deadline(5) do |d|
  loop do
    d.check!  # raises DeadlineExceeded if time is up
    process_next_item
  end
end

Remaining Time

Philiprehberger::TimeoutKit.deadline(10) do |d|
  while d.remaining > 1
    do_work
    d.check!
  end
  puts "Only #{d.remaining}s left, wrapping up"
end

Measuring elapsed time

Philiprehberger::TimeoutKit.deadline(10) do |d|
  do_work
  puts "Work took #{d.elapsed}s so far"
  # elapsed continues to grow past the budget even after expiration
end

Deadline Progress

deadline = Philiprehberger::TimeoutKit::Deadline.new(10)
sleep 5
deadline.progress # => 0.5 (approximately)

Nested Deadlines

Philiprehberger::TimeoutKit.deadline(30) do |outer|
  # Inner deadline is tighter, so it takes precedence
  Philiprehberger::TimeoutKit.deadline(5) do |inner|
    inner.check!
    puts inner.remaining  # <= 5
  end

  # After inner block, outer deadline is restored
  outer.check!
  puts outer.remaining  # <= 30
end

Deadline Naming

Philiprehberger::TimeoutKit.deadline(10, name: 'db_query') do |d|
  d.check!  # raises "Deadline 'db_query' exceeded" if expired
  puts d.name  # => "db_query"
end

Deadline Callbacks

Philiprehberger::TimeoutKit.deadline(10, on_expire: -> { cleanup() }) do |d|
  loop do
    d.check!  # fires callback once on first expiry detection
    process_next_item
  end
end

# Or register via block
Philiprehberger::TimeoutKit.deadline(10) do |d|
  d.on_expire { cleanup() }
  loop do
    d.check!
    process_next_item
  end
end

Grace Period

Philiprehberger::TimeoutKit.deadline(10, grace: 2) do |d|
  loop do
    d.check!  # does not raise during 2s grace period
    break if d.expired?

    process_next_item
  end

  if d.in_grace?
    puts "Grace period: #{d.grace_remaining}s left to wrap up"
  end
end

Cooperative Timeout

Philiprehberger::TimeoutKit.cooperative(5) do |t|
  items.each do |item|
    t.check!
    process(item)
  end
end

Current Deadline

Philiprehberger::TimeoutKit.deadline(10) do |_d|
  current = Philiprehberger::TimeoutKit.current_deadline
  puts current.remaining
end

API

Method Description
.deadline(seconds, name:, grace:, on_expire:) { |d| } Execute a block with a cooperative deadline
.cooperative(seconds) { |t| } Execute a block with a simple cooperative timeout
.current_deadline Return the current active deadline or nil
Deadline#check! Raise DeadlineExceeded if the deadline has passed (respects grace period)
Deadline#remaining Seconds remaining until the primary deadline (negative during grace)
Deadline#elapsed Seconds elapsed since the deadline was created (continues past the budget after expiration)
Deadline#duration The original budget passed to Deadline.new (Float)
Deadline#progress Fraction of the budget that has elapsed (elapsed / duration); exceeds 1.0 after expiry
Deadline#expired? Whether the primary deadline has passed
Deadline#name The human-readable name for this deadline (nil if not set)
Deadline#in_grace? Whether the deadline is in the grace period
Deadline#grace_remaining Seconds remaining in the grace period (0.0 if none)
Deadline#on_expire { } Register a callback that fires once on expiry detection
DeadlineExceeded Raised when a deadline or timeout expires

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT