1 /* 2 Copyright (c) 2005-2020 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "oneapi/tbb/detail/_exception.h" 18 #include "oneapi/tbb/detail/_assert.h" 19 #include "oneapi/tbb/detail/_template_helpers.h" 20 21 #include <cstring> 22 #include <cstdio> 23 #include <stdexcept> // std::runtime_error 24 #include <new> 25 #include <stdexcept> 26 27 #define __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN \ 28 (__GLIBCXX__ && __TBB_GLIBCXX_VERSION>=40700 && __TBB_GLIBCXX_VERSION<60000 && TBB_USE_EXCEPTIONS) 29 30 #if __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN 31 // GCC ABI declarations necessary for a workaround 32 #include <cxxabi.h> 33 #endif 34 35 namespace tbb { 36 namespace detail { 37 namespace r1 { 38 39 const char* bad_last_alloc::what() const noexcept(true) { return "bad allocation in previous or concurrent attempt"; } 40 const char* user_abort::what() const noexcept(true) { return "User-initiated abort has terminated this operation"; } 41 const char* missing_wait::what() const noexcept(true) { return "wait() was not called on the structured_task_group"; } 42 43 #if TBB_USE_EXCEPTIONS 44 template <typename F> 45 /*[[noreturn]]*/ void do_throw_noexcept(F throw_func) noexcept { 46 throw_func(); 47 } 48 49 /*[[noreturn]]*/ void do_throw_noexcept(void (*throw_func)()) noexcept { 50 throw_func(); 51 } 52 53 bool terminate_on_exception(); // defined in global_control.cpp and ipc_server.cpp 54 55 template <typename F> 56 /*[[noreturn]]*/ void do_throw(F throw_func) { 57 if (terminate_on_exception()) { 58 do_throw_noexcept(throw_func); 59 } 60 throw_func(); 61 } 62 63 #define DO_THROW(exc, init_args) do_throw( []{ throw exc init_args; } ); 64 #else /* !TBB_USE_EXCEPTIONS */ 65 #define PRINT_ERROR_AND_ABORT(exc_name, msg) \ 66 std::fprintf (stderr, "Exception %s with message %s would have been thrown, " \ 67 "if exception handling had not been disabled. Aborting.\n", exc_name, msg); \ 68 std::fflush(stderr); \ 69 std::abort(); 70 #define DO_THROW(exc, init_args) PRINT_ERROR_AND_ABORT(#exc, #init_args) 71 #endif /* !TBB_USE_EXCEPTIONS */ 72 73 void throw_exception ( exception_id eid ) { 74 switch ( eid ) { 75 case exception_id::bad_alloc: DO_THROW(std::bad_alloc, ()); break; 76 case exception_id::bad_last_alloc: DO_THROW(bad_last_alloc, ()); break; 77 case exception_id::user_abort: DO_THROW( user_abort, () ); break; 78 case exception_id::nonpositive_step: DO_THROW(std::invalid_argument, ("Step must be positive") ); break; 79 case exception_id::out_of_range: DO_THROW(std::out_of_range, ("Index out of requested size range")); break; 80 case exception_id::reservation_length_error: DO_THROW(std::length_error, ("Attempt to exceed implementation defined length limits")); break; 81 case exception_id::missing_wait: DO_THROW(missing_wait, ()); break; 82 case exception_id::invalid_load_factor: DO_THROW(std::out_of_range, ("Invalid hash load factor")); break; 83 case exception_id::invalid_key: DO_THROW(std::out_of_range, ("invalid key")); break; 84 case exception_id::bad_tagged_msg_cast: DO_THROW(std::runtime_error, ("Illegal tagged_msg cast")); break; 85 #if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE 86 case exception_id::unsafe_wait: DO_THROW(unsafe_wait, ("Unsafe to wait further")); break; 87 #endif 88 default: __TBB_ASSERT ( false, "Unknown exception ID" ); 89 } 90 __TBB_ASSERT(false, "Unreachable code"); 91 } 92 93 /* The "what" should be fairly short, not more than about 128 characters. 94 Because we control all the call sites to handle_perror, it is pointless 95 to bullet-proof it for very long strings. 96 97 Design note: ADR put this routine off to the side in tbb_misc.cpp instead of 98 Task.cpp because the throw generates a pathetic lot of code, and ADR wanted 99 this large chunk of code to be placed on a cold page. */ 100 void handle_perror( int error_code, const char* what ) { 101 const int BUF_SIZE = 255; 102 char buf[BUF_SIZE + 1] = { 0 }; 103 std::strncat(buf, what, BUF_SIZE); 104 std::size_t buf_len = std::strlen(buf); 105 if (error_code) { 106 std::strncat(buf, ": ", BUF_SIZE - buf_len); 107 buf_len = std::strlen(buf); 108 std::strncat(buf, std::strerror(error_code), BUF_SIZE - buf_len); 109 buf_len = std::strlen(buf); 110 } 111 __TBB_ASSERT(buf_len <= BUF_SIZE && buf[buf_len] == 0, nullptr); 112 #if TBB_USE_EXCEPTIONS 113 do_throw([buf] { throw std::runtime_error(buf); }); 114 #else 115 PRINT_ERROR_AND_ABORT( "runtime_error", buf); 116 #endif /* !TBB_USE_EXCEPTIONS */ 117 } 118 119 #if __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN 120 // Runtime detection and workaround for the GCC bug 62258. 121 // The problem is that std::rethrow_exception() does not increment a counter 122 // of active exceptions, causing std::uncaught_exception() to return a wrong value. 123 // The code is created after, and roughly reflects, the workaround 124 // at https://gcc.gnu.org/bugzilla/attachment.cgi?id=34683 125 126 void fix_broken_rethrow() { 127 struct gcc_eh_data { 128 void * caughtExceptions; 129 unsigned int uncaughtExceptions; 130 }; 131 gcc_eh_data* eh_data = punned_cast<gcc_eh_data*>( abi::__cxa_get_globals() ); 132 ++eh_data->uncaughtExceptions; 133 } 134 135 bool gcc_rethrow_exception_broken() { 136 bool is_broken; 137 __TBB_ASSERT( !std::uncaught_exception(), 138 "gcc_rethrow_exception_broken() must not be called when an exception is active" ); 139 try { 140 // Throw, catch, and rethrow an exception 141 try { 142 throw __TBB_GLIBCXX_VERSION; 143 } catch(...) { 144 std::rethrow_exception( std::current_exception() ); 145 } 146 } catch(...) { 147 // Check the bug presence 148 is_broken = std::uncaught_exception(); 149 } 150 if( is_broken ) fix_broken_rethrow(); 151 __TBB_ASSERT( !std::uncaught_exception(), NULL ); 152 return is_broken; 153 } 154 #else 155 void fix_broken_rethrow() {} 156 bool gcc_rethrow_exception_broken() { return false; } 157 #endif /* __TBB_STD_RETHROW_EXCEPTION_POSSIBLY_BROKEN */ 158 159 } // namespace r1 160 } // namespace detail 161 } // namespace tbb 162 163