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

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\
`.cooperative(seconds) { \ t\
.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#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