Ruby Exception Handling

Ruby Exception Handling: ThreadError

Making our way through the Ruby Exception Handling series we have now arrived at the ThreadError class. ThreadErrors are raised anytime an invalid operation is attempted on a thread, plain and (somewhat) simple!

Throughout this article we’ll examine the ThreadError in a bit more detail, looking at where it sits in the Ruby Exception class hierarchy and examining some simple example code that illustrates how ThreadErrors are raised so you can (hopefully) avoid them in your own coding adventures. Let’s get this party started!

The Technical Rundown

  • All Ruby exceptions are descendants of the Exception class, or a subclass therein.
  • StandardError is a direct descendant of the Exception class, and is also a superclass with many descendants of its own.
  • ThreadError is the direct descendant of StandardError.

When Should You Use It?

As previously mentioned a ThreadError is the result of trying to perform an invalid operation on a thread in Ruby. Yet working with threads can be rather confusing so let’s start with a brief overview of how we can use threading to handle simultaneous processing in our Ruby code.

The Thread class provides a number of helper API methods, but since the purpose of using multiple threads is to perform concurrent actions we’ll typically just start by instantiating a new thread with Thread::new:

Our new thread has a code block that tells it to execute a single statement of puts for our string message. However, merely instantiating a new thread doesn’t cause it to execute. As is normal, when executing any Ruby script we always start (and finish) with the main thread. To get our new sub-thread to execute we need to join it to an active thread. This can be accomplished with the #join method of the sub-thread we want to start. This suspends the current thread execution (our main thread in this case) and starts execution of the sub-thread:

Since our code block for what the new thread should accomplish is so short we immediately get our message output and then the sub-thread is killed and the main thread resumes.

To expand on this a bit and see how ThreadErrors are produced we’ll take a look at some slightly more complex code that uses threading. Here we have a few helper functions and modules, but the main functions we care about are #get_threads and #thread_example:

#get_threads creates a couple new sub-threads places them into an array that is returned. We also output a few log messages so we know when sub-thread 1 begins and ends its two-second sleep cycle. #thread_example is triggered by the main thread and takes our collection of sub-threads and #joins them with the main thread. The expected result is that we should see the main thread start, then both our sub-threads terminate, and finally our main thread should terminate at the end. Sure enough, running this code shows us that expected output:

We didn’t really try to do anything fancy with our threads in this example, so everything ran as expected, but what happens if we try to mix things up a bit and perform some actions on our threads that may be invalid or out of order? For example, the overall setup is the same where we can get a few sub-threads via #get_threads, but this time let’s try calling the Thread::stop method while we’re looping through our sub-threads to #join them up:

Suddenly we run into trouble and a ThreadError is thrown, indicating that we’ve attempted to stop the only active thread (our main thread):

Notice that we actually get all the output messages indicating our sub-threads have executed as expected before we get the ThreadError. This is because of the ordering that we’re executing everything in. The #join call suspends the main thread temporarily and processes the sub-thread that is being called, so only after both of those terminate does the Thread::stop call actually run into trouble, which is indicated by the error message — we cannot stop a thread when it’s the only active thread at the time.

To get the most out of your own applications and to fully manage any and all Ruby Exceptions, check out the Airbrake Ruby exception handling tool, offering real-time alerts and instantaneous insight into what went wrong with your Ruby code, including integrated support for a variety of popular Ruby gems and frameworks.