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