Here’s something that’s perhaps not entirely obvious: when you call
Thread#raise, the exception will be raised at whatever point of execution that thread happens to be at.
See how the stack trace in the exception shows a different line of code each time? It’s understandable that it would behave like this — I can’t think of any other behavior that would make more sense. But it should only be used when the code inside the thread knows that it’s being called from a particular outer context, and that the outer context might have its own problems that it them tells the thread about. Example:
Unfortunately, Ruby’s timeout library uses
Thread#raise, even though it is frequently used around enormous amounts of arbitrary code and libraries that might have no idea that they are being called within
Timeout#timeout and can’t be expected to elegantly handle a timeout event at every single point in the code. Furthermore, this inner code might have blocks that call top-level
Exception, which will then catch the exception that
Timeout sends, handle it in a way it wasn’t written for, and not raise it back up to the
Timeout.timeout invocation. I’ll elaborate on this issue more in future posts.