Who calls std::terminate?

In some situations exception handling must be abandoned for less subtle error handling techniques; these are handled by calling std::terminate. In this post I list all the situations where std::terminate can be called. In C++11 this list grows bigger. This is a good opportunity to go through various new features of C++11.

When is std::terminate called?

First, and most notably, std::terminate is called when someöne throws an exception, and there is no compatible catch to be found. This can happen for three reasons:

  1. You did not put an exception handler (catch statement) that would prevent an exception from leaving function main.
  2. Similarly, you did not put a proper exception handler that would prevent exception from getting out of initial function of a thread.
  3. An exception was thrown from a place where it is simply impossible to put any exception handler.

This is one of the main advantages of exceptions over return codes, or errno-like solutions: if you forget about handling an error, it will not go unnoticed, possibly corrupting your application’s state, but will be handled with the most reasonable and universal default: std::terminate.

Note that in the first two cases, when an exception is thrown in the program and the run-time knows that it will not be caught before it leaves main or initial function of spawned thread, it may skip calling the destructors of automatic objects. People sometimes wonder why their destructor was not called in the following code.

struct Messanger {
    ~Messanger() {
        std::cout << "Very important message" << std::endl;
    }
};

int main() {
    Messanger m;
    throw std::runtime_error("");
}

C++ allows compilers or runtimes not to call destructors in such cases in order for them to be able to provide more efficient implementations of exception handling mechanism. Therefore, programmers are often encouraged to wrap function main with exception handlers that catch every possible exception:

int main() 
try {
    // do what you want
}
catch( std::exception & exc ) {
    // use exc.what()
}
catch( ... ) {
    // ensure destuctors of auto objects are called
}

Not to (or, not only to) prevent calling std::terminate, but to make sure all destructors of automatic objects have been called.

Similar situation occurs when initial function of a thread throws exception. Initial function of a thread is in many aspects similar to function main for the main thread. The only difference is that sometimes it is better to handle any error thrown in the child thread, in the main thread. Therefore C++ provides facility for storing an exception thrown in one thread and re-throwing it in another. We describe it in a bit more detail later in the post.

I mentioned places “where it is impossible to put any exception handler”. Those places are:

  • initialization of a global object, thread-local object, or static class member;
  • destruction of a global object, thread-local object, static class member, or function-local static object;
  • execution of a function registered with std::atexit or std::at_quick_exit.

We are already encouraged not to throw from destructors, so the second case would rarely be hit. The third case is equally unlikely because functions registered with std::atexit are very similar to destructors of global objects (in fact some compilers implement the calls of globals’ destructors by registering them with std::atexit), and the same safety rules that apply to destructors apply to those clean-up functions called at program/thread exit. However, the case of the initialization of globals is different. Initialization can often fail; in fact it is the initialization that will fail more often than any other action. Therefore, a couple of rules should be considered when initializing globals:

  1. If possible, avoid using globals and similar; they cause many problems: prevent referential transparency, make unit testing more difficult, cause problems when initialization fails, cause order-of-destruction problems.
  2. If possible, use only built-in types (int, float) for globals and rely only on the default zero-initialization or initialize with literals (like 1.0) — it cannot fail.
  3. If some computation is required, prefer using compile-time initialization, which can be enforced with constexpr — this is what constexpr is for; it cannot fail at run time. (constexpr is only a C++11 feature, in C++03 you have to resort to template meta-programming to achieve a similar goal)
  4. If some non-trivial runtime computations are required, consider creating an automatic object at the top level of function main or use Singleton pattern or a function-local static instead — they are initialized only at the first use, which is likely to be inside main, where exception handlers can be installed.

Next reason for calling std::terminate is when any function executed during stack unwinding exits via an exception. Typically, this would be a throwing destructor of automatic object, but there are different functions that may throw during stack unwinding: it may be a copy constructor, or destructor of the exception object that we are throwing. Consider the following exception handler:

catch( MyException exc ) {
    // ...
}

The exception is caught by value. If this handler is considered a match for the active exception, a copy constructor of MyException is invoked. If it throws an exception, the handler is not considered active yet, and stack unwinding is not finished yet: the second exception causes a call to std::terminate. Destructors aside, we could say that exceptions’ copy constructors should not throw, but this would be hard to achieve. Copy consructors usually may throw. If you use std::strings in your exceptions, they may throw on copying. Alternative advice would be to always catch exceptions by reference (possibly to const). This also avoids an additional problem of exception slicing.

Now, to throwing destructors. In C++11, unless you explicitly specify otherwise, all destructors are implicitly declared as noexcept. You might think that it will reduce the chance that std::terminate is called. In fact, it is just the opposite. Annotating your function with noexcept means “call std::terminate when I throw”. In C++03 std::terminate was called only when destructor threw during stack unwinding. In C++11 std::terminate is called always when destructor throws. In fact, this change may be helpful. We will explain that in the next post.

In general, any function annotated with noexcept calls std::terminate when it attempts to throw. There are two sides of using noexcept (you can read about it here); you get run-time speed-up, but you risk calling std::terminate.

Note that in case of violating noexcept specification, the compiler/runtime is allowed not to unwind the stack, or only to partially unwind the stack. This allows more efficient implementations of noexcept feature, and allows better error reporting, as described in the next post.

Next erroneous situation is when a “re-throw” expression is evaluated, but there is no exception currently handled that could be re-thrown. This is how you can cause such situation:

int main() {
    throw;
}

You might ask, why does compiler allow putting a re-throw expression outside of an exception handler. In general, without some global program flow analysis, it is impossible to tell if a re-throw is inside some exception handler or not, for instance the following is absolutely correct, although we re-throw in a function without any try-catch:

[[noreturn]] void rethrow() {
    throw;
}

void fun()
try {
    // do something...
}
catch( ... ) {
    rethrow();
}

A similar bad re-throw situation, which also triggers std::terminate, occurs when the function std::nested_exception::rethrow_nested is called for an object that has captured no exception.

This may require a word of explanation. rethrow_nested is part of new functionality in C++11 that can be used for nesting exceptions inside other exceptions. The idea is that if we translate an exception, that is, catch one exception (a low-level one) and throw a different one (high-level), we still want to make the original one available if someone that catches the new exception wishes to inspect the original. So we can nest the low-level exception inside the high-level one. Later, when we catch the high-level exception and handle it somehow, we may still want to re-throw the nested, low-level exception. We can use function rethrow_nested for that purpose, but if it so happens that there is no nested exception to be re-thrown, and we forgot to check it, std::terminate needs to intervene. I could not find the functionality for nesting exceptions described anywhere in the web, except for these two ISO Committee proposals: N2509 and N2559, so I guess this could be a topic for my next post.

There is a third way to re-throw an exception; when we call function std::rethrow_exception. This is part of yet another functionality for saving the caught exception and re-throwing it later, possibly in different thread. The main motivation for this functionality is, in case a thread we launch cannot execute the function due to an exception, to be able to re-throw the same exception in the launching (master) thread as if it was thrown by master thread in the first place. std::future uses this mechanism. But this re-throw could also be called incorrectly if there is no exception to be re-thrown. In this case however, C++ Standard defines it as undefined behavior. This means that the compiler can choose whatever it wants to do in such case. (One of the possible choices would be to call std::terminate.)

Next two situations that trigger std::terminate, are connected with the call to a different handler function: std::unexpected. The latter function is always called when an exception thrown out of given function violates its dynamic-exception-specification. This is a new name in C++11 for old function exception specifications like:

void fun1() throw( ExceptionA, ExceptionB );
void fun2() throw(); // throw nothing

These dynamic-exception-specifications are deprecated in C++11, and so is function std::unexpected. Some compilers do not even implement it.

Unless you customize your unexpected handler, it will call std::terminate. Even if you customize it, one of the goals of such handler is to translate a disallowed exception into one that is allowed by exception specification. If the translated exception is still disallowed by the specification, exception handling mechanism attempts to recover by throwing std::bad_exception instead. But this special exception may still not be listed in function’s exception specification. If so, we resort to calling std::terminate. It can also happen that custom unexpected handler simply returns (does not exit by throwing exception), although it is not allowed to. C++ Standard defines such situation as undefined behavior.

Next candidate for calling std::terminate is really interesting, because it is not connected to exception handling mechanism. It occurs when a destructor of std::thread is called but the thread represented by the object is still joinable, i.e., has neither been joined with or detached from.

In C++11, objects of class std::thread represent a handle to operating system thread. The intended usage is that you launch a new thread if needed, and by the time the life-time of the handle ends you should make a call if you (the launching thread) want to join with (wait for) the forked thread, or detach from it. You should make it explicit what your intention is. In Boost.Thread library you have a third option which is a good default: thread interruption, which signals the thread that it should stop as soon as it can (possibly doing some necessary clean-up), and then we can join. Unfortunately C++11 does not give you the ability to interrupt a thread. Neither of the two remaining options is a satisfactory default: silently joining would cause unexpected and arbitrarily long pauses in the program, silently detaching would cause hard to debug situations, as explained here.

Thus not having joined with or detached from a thread before destruction of the handle is a programmer error, hard to avoid given that an intended call to join or detach could be skipped in case an exception is thrown. Errors would be typically signaled by throwing an exception, but it is not a good practice to throw from a destructor, so instead the destructor calls std::terminate. This is an example of how std::terminate can be used to simply signal a serious bug in the application; perhaps too serious for normal exception handling mechanism.

Does that make you want to avoid using std::thread altogether? Good. Unless you are an expert, that has to deal with such a low-level concept, C++11 offers a safer and nicer alternative: function async.

Note that std::terminate is also called when you move-assign to a joinable std::thread. This is because move assigning to an object is semantically equivalent to calling a destructor, and then move-constructing an object again.

Last, similarly to what we have seen in case of threads, you may call std::terminate yourself, if you feel that a given error condition is too serious for the program to simply throw an exception (because destructors cannot be safely run), and calling std::abort is not proper because you need or want to call manually installed terminate handler.

And that’s it. We listed all the cases where std::terminate is called. In the next article I will try to explore what useful things we can do inside std::terminate, go over some technical details and rationale behind the mechanism, and try to present my view that programmers are sometimes too much afraid of the fact that std::terminate might be called.

This entry was posted in programming and tagged , , . Bookmark the permalink.

3 Responses to Who calls std::terminate?

  1. Pingback: Questions: Advanced C and C++ | GO 2 Computer Science and Trading Technologies

  2. Pingback: c++ - anular, cancelar o salir?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.