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.
- OS-level timeouts on blocking I/O (
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 sandboxedeval; 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.Contextwithctx.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.
- .NET
- These approaches make cancellation explicit and allow cleanup, but require discipline and library support.
Language-Specific Behaviors and Comparisons
- Java:
Thread.stopis deprecated but still exists;InterruptedExceptionis 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_cancelillustrate 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.