Ruby's Timeout is dangerous and Thread.raise is terrifying (2015)

Alternatives to Ruby Timeout / Thread.raise

  • Many argue there is no generally safe way to abort arbitrary code inside a thread; you must redesign around cancellation.
  • Suggested safer patterns:
    • OS-level timeouts on blocking I/O (poll, select, epoll, etc.) instead of asynchronous exceptions.
    • Single-thread-per-process web workers; on timeout, kill and restart the whole process.
    • For untrusted or fragile code: fork, run work in a child, and kill the child on timeout.

Processes vs Threads for Time-Bounded Work

  • Processes provide stronger isolation than threads, making forced termination less likely to corrupt shared state.
  • Examples: subprocess wrappers for rg, sqlite, dbus, camera drivers, or sandboxed eval; often found more reliable than thread-based approaches.
  • Caveat: processes can still leave external state (files, DBs) inconsistent unless the app is designed for “crash-only” behavior with transactional semantics.

Cooperative Cancellation Patterns

  • Multiple ecosystems use explicit, cooperative cancellation:
    • .NET CancellationToken.
    • Go’s context.Context with ctx.Done(), often checked in loops and I/O; requires all libraries to opt in.
    • Reactive streams and similar APIs that expose a cancel event and cleanup hooks.
  • These approaches make cancellation explicit and allow cleanup, but require discipline and library support.

Language-Specific Behaviors and Comparisons

  • Java: Thread.stop is deprecated but still exists; InterruptedException is safer but frequently mishandled or swallowed.
  • Haskell and some Scala libraries (Cats Effect) offer async exceptions plus masking/“uncancelable” regions to protect critical sections; still tricky but seen as relatively strong.
  • Erlang/Elixir: isolated processes, supervision trees, and ports make killing misbehaving processes more manageable, though subtleties remain (e.g., IEx trapping exits).
  • Rust async: futures can be dropped at await points; destructors help with resource cleanup, but higher-level logic can still break.
  • C/C++: signal safety and pthread_cancel illustrate similar dangers of async interruption.

General Lessons on Timeouts and Interruption

  • Reliable timeouts are fundamentally a design problem, not a library call:
    • Code must be written assuming failure or cancellation at almost any point.
    • Good error and timeout handling are part of overall UX/API design.
  • Many conclude that “raise from the outside” is inherently dangerous; safer options rely on structured concurrency, explicit cancellation, and process-level isolation.