xref: /oneTBB/src/tbb/exception.cpp (revision c21e688a)
151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1749e08aacStbbdev #include "oneapi/tbb/detail/_exception.h"
1849e08aacStbbdev #include "oneapi/tbb/detail/_assert.h"
1949e08aacStbbdev #include "oneapi/tbb/detail/_template_helpers.h"
2051c0b2f7Stbbdev 
2151c0b2f7Stbbdev #include <cstring>
2251c0b2f7Stbbdev #include <cstdio>
2351c0b2f7Stbbdev #include <stdexcept> // std::runtime_error
2451c0b2f7Stbbdev #include <new>
2551c0b2f7Stbbdev #include <stdexcept>
2651c0b2f7Stbbdev 
2751c0b2f7Stbbdev #define __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN                             \
2851c0b2f7Stbbdev     (__GLIBCXX__ && __TBB_GLIBCXX_VERSION>=40700 && __TBB_GLIBCXX_VERSION<60000 && TBB_USE_EXCEPTIONS)
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev #if __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN
3151c0b2f7Stbbdev // GCC ABI declarations necessary for a workaround
3251c0b2f7Stbbdev #include <cxxabi.h>
3351c0b2f7Stbbdev #endif
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev namespace tbb {
3651c0b2f7Stbbdev namespace detail {
3751c0b2f7Stbbdev namespace r1 {
3851c0b2f7Stbbdev 
what() const3951c0b2f7Stbbdev const char* bad_last_alloc::what() const noexcept(true) { return "bad allocation in previous or concurrent attempt"; }
what() const4051c0b2f7Stbbdev const char* user_abort::what() const noexcept(true) { return "User-initiated abort has terminated this operation"; }
what() const4151c0b2f7Stbbdev const char* missing_wait::what() const noexcept(true) { return "wait() was not called on the structured_task_group"; }
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
4451c0b2f7Stbbdev     template <typename F>
do_throw_noexcept(F throw_func)4551c0b2f7Stbbdev     /*[[noreturn]]*/ void do_throw_noexcept(F throw_func) noexcept {
4651c0b2f7Stbbdev         throw_func();
4751c0b2f7Stbbdev     }
4851c0b2f7Stbbdev 
do_throw_noexcept(void (* throw_func)())4951c0b2f7Stbbdev     /*[[noreturn]]*/ void do_throw_noexcept(void (*throw_func)()) noexcept {
5051c0b2f7Stbbdev         throw_func();
51478de5b1Stbbdev #if __GNUC__ == 7
52478de5b1Stbbdev         // In release, GCC 7 loses noexcept attribute during tail call optimization.
53478de5b1Stbbdev         // The following statement prevents tail call optimization.
54478de5b1Stbbdev         volatile bool reach_this_point = true;
55478de5b1Stbbdev         suppress_unused_warning(reach_this_point);
56478de5b1Stbbdev #endif
5751c0b2f7Stbbdev     }
5851c0b2f7Stbbdev 
5951c0b2f7Stbbdev     bool terminate_on_exception(); // defined in global_control.cpp and ipc_server.cpp
6051c0b2f7Stbbdev 
6151c0b2f7Stbbdev     template <typename F>
do_throw(F throw_func)6251c0b2f7Stbbdev     /*[[noreturn]]*/ void do_throw(F throw_func) {
6351c0b2f7Stbbdev         if (terminate_on_exception()) {
6451c0b2f7Stbbdev             do_throw_noexcept(throw_func);
6551c0b2f7Stbbdev         }
6651c0b2f7Stbbdev         throw_func();
6751c0b2f7Stbbdev     }
6851c0b2f7Stbbdev 
6951c0b2f7Stbbdev     #define DO_THROW(exc, init_args) do_throw( []{ throw exc init_args; } );
7051c0b2f7Stbbdev #else /* !TBB_USE_EXCEPTIONS */
7151c0b2f7Stbbdev     #define PRINT_ERROR_AND_ABORT(exc_name, msg) \
7251c0b2f7Stbbdev         std::fprintf (stderr, "Exception %s with message %s would have been thrown, "  \
7351c0b2f7Stbbdev             "if exception handling had not been disabled. Aborting.\n", exc_name, msg); \
7451c0b2f7Stbbdev         std::fflush(stderr); \
7551c0b2f7Stbbdev         std::abort();
7651c0b2f7Stbbdev     #define DO_THROW(exc, init_args) PRINT_ERROR_AND_ABORT(#exc, #init_args)
7751c0b2f7Stbbdev #endif /* !TBB_USE_EXCEPTIONS */
7851c0b2f7Stbbdev 
throw_exception(exception_id eid)7951c0b2f7Stbbdev void throw_exception ( exception_id eid ) {
8051c0b2f7Stbbdev     switch ( eid ) {
8151c0b2f7Stbbdev     case exception_id::bad_alloc: DO_THROW(std::bad_alloc, ()); break;
8251c0b2f7Stbbdev     case exception_id::bad_last_alloc: DO_THROW(bad_last_alloc, ()); break;
8351c0b2f7Stbbdev     case exception_id::user_abort: DO_THROW( user_abort, () ); break;
8451c0b2f7Stbbdev     case exception_id::nonpositive_step: DO_THROW(std::invalid_argument, ("Step must be positive") ); break;
8551c0b2f7Stbbdev     case exception_id::out_of_range: DO_THROW(std::out_of_range, ("Index out of requested size range")); break;
8651c0b2f7Stbbdev     case exception_id::reservation_length_error: DO_THROW(std::length_error, ("Attempt to exceed implementation defined length limits")); break;
8751c0b2f7Stbbdev     case exception_id::missing_wait: DO_THROW(missing_wait, ()); break;
8851c0b2f7Stbbdev     case exception_id::invalid_load_factor: DO_THROW(std::out_of_range, ("Invalid hash load factor")); break;
8951c0b2f7Stbbdev     case exception_id::invalid_key: DO_THROW(std::out_of_range, ("invalid key")); break;
9051c0b2f7Stbbdev     case exception_id::bad_tagged_msg_cast: DO_THROW(std::runtime_error, ("Illegal tagged_msg cast")); break;
9151c0b2f7Stbbdev     case exception_id::unsafe_wait: DO_THROW(unsafe_wait, ("Unsafe to wait further")); break;
9251c0b2f7Stbbdev     default: __TBB_ASSERT ( false, "Unknown exception ID" );
9351c0b2f7Stbbdev     }
9451c0b2f7Stbbdev     __TBB_ASSERT(false, "Unreachable code");
9551c0b2f7Stbbdev }
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev /* The "what" should be fairly short, not more than about 128 characters.
9851c0b2f7Stbbdev    Because we control all the call sites to handle_perror, it is pointless
9951c0b2f7Stbbdev    to bullet-proof it for very long strings.
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev    Design note: ADR put this routine off to the side in tbb_misc.cpp instead of
10251c0b2f7Stbbdev    Task.cpp because the throw generates a pathetic lot of code, and ADR wanted
10351c0b2f7Stbbdev    this large chunk of code to be placed on a cold page. */
handle_perror(int error_code,const char * what)10451c0b2f7Stbbdev void handle_perror( int error_code, const char* what ) {
10551c0b2f7Stbbdev     const int BUF_SIZE = 255;
10651c0b2f7Stbbdev     char buf[BUF_SIZE + 1] = { 0 };
10751c0b2f7Stbbdev     std::strncat(buf, what, BUF_SIZE);
10851c0b2f7Stbbdev     std::size_t buf_len = std::strlen(buf);
10951c0b2f7Stbbdev     if (error_code) {
11051c0b2f7Stbbdev         std::strncat(buf, ": ", BUF_SIZE - buf_len);
11151c0b2f7Stbbdev         buf_len = std::strlen(buf);
11251c0b2f7Stbbdev         std::strncat(buf, std::strerror(error_code), BUF_SIZE - buf_len);
11351c0b2f7Stbbdev         buf_len = std::strlen(buf);
11451c0b2f7Stbbdev     }
11551c0b2f7Stbbdev     __TBB_ASSERT(buf_len <= BUF_SIZE && buf[buf_len] == 0, nullptr);
11651c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
117b15aabb3Stbbdev     do_throw([&buf] { throw std::runtime_error(buf); });
11851c0b2f7Stbbdev #else
11951c0b2f7Stbbdev     PRINT_ERROR_AND_ABORT( "runtime_error", buf);
12051c0b2f7Stbbdev #endif /* !TBB_USE_EXCEPTIONS */
12151c0b2f7Stbbdev }
12251c0b2f7Stbbdev 
12351c0b2f7Stbbdev #if __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN
12451c0b2f7Stbbdev // Runtime detection and workaround for the GCC bug 62258.
12551c0b2f7Stbbdev // The problem is that std::rethrow_exception() does not increment a counter
12651c0b2f7Stbbdev // of active exceptions, causing std::uncaught_exception() to return a wrong value.
12751c0b2f7Stbbdev // The code is created after, and roughly reflects, the workaround
12851c0b2f7Stbbdev // at https://gcc.gnu.org/bugzilla/attachment.cgi?id=34683
12951c0b2f7Stbbdev 
fix_broken_rethrow()13051c0b2f7Stbbdev void fix_broken_rethrow() {
13151c0b2f7Stbbdev     struct gcc_eh_data {
13251c0b2f7Stbbdev         void *       caughtExceptions;
13351c0b2f7Stbbdev         unsigned int uncaughtExceptions;
13451c0b2f7Stbbdev     };
13551c0b2f7Stbbdev     gcc_eh_data* eh_data = punned_cast<gcc_eh_data*>( abi::__cxa_get_globals() );
13651c0b2f7Stbbdev     ++eh_data->uncaughtExceptions;
13751c0b2f7Stbbdev }
13851c0b2f7Stbbdev 
gcc_rethrow_exception_broken()13951c0b2f7Stbbdev bool gcc_rethrow_exception_broken() {
14051c0b2f7Stbbdev     bool is_broken;
14151c0b2f7Stbbdev     __TBB_ASSERT( !std::uncaught_exception(),
14251c0b2f7Stbbdev         "gcc_rethrow_exception_broken() must not be called when an exception is active" );
14351c0b2f7Stbbdev     try {
14451c0b2f7Stbbdev         // Throw, catch, and rethrow an exception
14551c0b2f7Stbbdev         try {
14651c0b2f7Stbbdev             throw __TBB_GLIBCXX_VERSION;
14751c0b2f7Stbbdev         } catch(...) {
14851c0b2f7Stbbdev             std::rethrow_exception( std::current_exception() );
14951c0b2f7Stbbdev         }
15051c0b2f7Stbbdev     } catch(...) {
15151c0b2f7Stbbdev         // Check the bug presence
15251c0b2f7Stbbdev         is_broken = std::uncaught_exception();
15351c0b2f7Stbbdev     }
15451c0b2f7Stbbdev     if( is_broken ) fix_broken_rethrow();
15557f524caSIlya Isaev     __TBB_ASSERT( !std::uncaught_exception(), nullptr);
15651c0b2f7Stbbdev     return is_broken;
15751c0b2f7Stbbdev }
15851c0b2f7Stbbdev #else
fix_broken_rethrow()15951c0b2f7Stbbdev void fix_broken_rethrow() {}
gcc_rethrow_exception_broken()16051c0b2f7Stbbdev bool gcc_rethrow_exception_broken() { return false; }
16151c0b2f7Stbbdev #endif /* __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN */
16251c0b2f7Stbbdev 
16351c0b2f7Stbbdev } // namespace r1
16451c0b2f7Stbbdev } // namespace detail
16551c0b2f7Stbbdev } // namespace tbb
16651c0b2f7Stbbdev 
167