xref: /oneTBB/src/tbb/exception.cpp (revision d86ed7fb)
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