]>
Commit | Line | Data |
---|---|---|
7adcbafe | 1 | /* Copyright (C) 2009-2022 Free Software Foundation, Inc. |
0a35513e AH |
2 | Contributed by Richard Henderson <rth@redhat.com>. |
3 | ||
4 | This file is part of the GNU Transactional Memory Library (libitm). | |
5 | ||
6 | Libitm is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | Libitm is distributed in the hope that it will be useful, but WITHOUT ANY | |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | more details. | |
15 | ||
16 | Under Section 7 of GPL version 3, you are granted additional | |
17 | permissions described in the GCC Runtime Library Exception, version | |
18 | 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | You should have received a copy of the GNU General Public License and | |
21 | a copy of the GCC Runtime Library Exception along with this program; | |
22 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | <http://www.gnu.org/licenses/>. */ | |
24 | ||
25 | #include "libitm_i.h" | |
26 | ||
27 | using namespace GTM; | |
28 | ||
258c1d07 TR |
29 | /* Exceptions can exist in three phases: (1) after having been allocated by |
30 | __cxa_allocate_exception but before being handed off to __cxa_throw, | |
31 | (2) when they are in flight, so between __cxa_throw and __cxa_begin_catch, | |
32 | and (3) when they are being handled (between __cxa_begin_catch and | |
33 | __cxa_end_catch). Note that when an exception is re-thrown in (3), it is | |
34 | not moving back to (2) but handled as a special case of (3) by the EH | |
35 | runtime. | |
36 | ||
37 | We can get aborts in all three phases, for example in (1) during | |
38 | construction of the exception object, or in (2) in destructors called | |
39 | while unwinding the stack. The transaction that created an exception | |
40 | object can only commit in phase (3) by re-throwing the exception; it cannot | |
41 | commit in other phases because throw expressions and catch clauses are | |
42 | properly nested wrt transactions and because the compiler wraps | |
43 | transaction bodies in a try/catch-all construct. | |
44 | ||
45 | We handle phase (1) by dealing with exception objects similar to how we | |
46 | deal with other (de)allocations, which also ensures that we can have more | |
47 | than one exception object allocated at the same time (e.g., if the | |
48 | throw expression itself throws an exception and thus calls | |
49 | __cxa_allocate_exception). However, on the call to __cxa_begin_catch | |
50 | we hand off the exception to the special handling of phase (3) and | |
51 | remove the undo log entry of the allocation. Note that if the allocation | |
52 | happened outside of this transaction, we do not need to do anything. | |
53 | ||
54 | When an exception reaches phase (2) due to a call to __cxa_throw, the count | |
55 | of uncaught exceptions is incremented. We roll back this effect by saving | |
56 | and restoring this number in the structure returned from __cxa_get_globals. | |
57 | This also takes care of increments of this count when re-throwing an | |
58 | exception. | |
59 | ||
60 | For phase (3), we keep track of the number of times __cxa_begin_catch | |
61 | has been called without a matching call to __cxa_end_catch. This count | |
62 | is then used by __cxa_tm_cleanup to roll back the exception handling state | |
63 | by calling __cxa_end_catch for the exceptions that have not been finished | |
64 | yet (without running destructors though because we roll back the memory | |
65 | anyway). | |
66 | Once an exception that was allocated in this transaction enters phase (3), | |
67 | it does not need to be deallocated on abort anymore because the calls to | |
68 | __cxa_end_catch will take care of that. | |
69 | ||
70 | We require all code executed by the transaction to be transaction_safe (or | |
71 | transaction_pure, or to have wrappers) if the transaction is to be rolled | |
72 | back. However, we take care to not require this for transactions that | |
73 | just commit; this way, transactions that enter serial mode and then call | |
74 | uninstrumented code continue to work. | |
75 | */ | |
76 | ||
0a35513e AH |
77 | /* Everything from libstdc++ is weak, to avoid requiring that library |
78 | to be linked into plain C applications using libitm.so. */ | |
79 | ||
80 | #define WEAK __attribute__((weak)) | |
81 | ||
82 | extern "C" { | |
83 | ||
258c1d07 TR |
84 | struct __cxa_eh_globals |
85 | { | |
86 | void * caughtExceptions; | |
87 | unsigned int uncaughtExceptions; | |
88 | }; | |
89 | ||
f0de5d83 NS |
90 | extern void *__cxa_allocate_exception (size_t) _ITM_NOTHROW WEAK; |
91 | extern void __cxa_free_exception (void *) _ITM_NOTHROW WEAK; | |
784417d1 | 92 | extern void __cxa_throw (void *, void *, void (*) (void *)) WEAK; |
f0de5d83 | 93 | extern void *__cxa_begin_catch (void *) _ITM_NOTHROW WEAK; |
f2f9a097 | 94 | extern void __cxa_end_catch (void) WEAK; |
f0de5d83 NS |
95 | extern void __cxa_tm_cleanup (void *, void *, unsigned int) throw () WEAK; |
96 | extern __cxa_eh_globals *__cxa_get_globals (void) _ITM_NOTHROW WEAK; | |
0a35513e | 97 | |
6c59ffd1 | 98 | #if !defined (HAVE_ELF_STYLE_WEAKREF) |
f0de5d83 NS |
99 | void *__cxa_allocate_exception (size_t) _ITM_NOTHROW { return NULL; } |
100 | void __cxa_free_exception (void *) _ITM_NOTHROW { return; } | |
784417d1 | 101 | void __cxa_throw (void *, void *, void (*) (void *)) { return; } |
f0de5d83 | 102 | void *__cxa_begin_catch (void *) _ITM_NOTHROW { return NULL; } |
f2f9a097 | 103 | void __cxa_end_catch (void) { return; } |
f0de5d83 | 104 | void __cxa_tm_cleanup (void *, void *, unsigned int) throw () { return; } |
8377e5e5 | 105 | void _Unwind_DeleteException (_Unwind_Exception *) { return; } |
f0de5d83 | 106 | __cxa_eh_globals *__cxa_get_globals (void) _ITM_NOTHROW { return NULL; } |
8cf36bb3 | 107 | #endif /* HAVE_ELF_STYLE_WEAKREF */ |
d846e425 | 108 | |
0a35513e AH |
109 | } |
110 | ||
258c1d07 TR |
111 | static void |
112 | free_any_exception (void *exc_ptr) | |
113 | { | |
114 | // The exception could be in phase (2) and thus calling just | |
115 | // _cxa_free_exception might not be sufficient. | |
116 | __cxa_tm_cleanup (NULL, exc_ptr, 0); | |
117 | } | |
0a35513e AH |
118 | |
119 | void * | |
f0de5d83 | 120 | _ITM_cxa_allocate_exception (size_t size) _ITM_NOTHROW |
0a35513e AH |
121 | { |
122 | void *r = __cxa_allocate_exception (size); | |
258c1d07 | 123 | gtm_thr()->record_allocation (r, free_any_exception); |
0a35513e AH |
124 | return r; |
125 | } | |
126 | ||
258c1d07 | 127 | void |
f0de5d83 | 128 | _ITM_cxa_free_exception (void *exc_ptr) _ITM_NOTHROW |
258c1d07 TR |
129 | { |
130 | // __cxa_free_exception can be called from user code directly if | |
131 | // construction of an exception object throws another exception, in which | |
132 | // case we need to roll back the initial exception. We handle this similar | |
133 | // to dead allocations in that we deallocate the exception on both commit | |
134 | // and abort of an outermost transaction. | |
135 | gtm_thr()->forget_allocation (exc_ptr, free_any_exception); | |
136 | } | |
137 | ||
0a35513e | 138 | void |
784417d1 | 139 | _ITM_cxa_throw (void *obj, void *tinfo, void (*dest) (void *)) |
0a35513e | 140 | { |
258c1d07 | 141 | // This used to be instrumented, but does not need to be anymore. |
0a35513e AH |
142 | __cxa_throw (obj, tinfo, dest); |
143 | } | |
144 | ||
145 | void * | |
f0de5d83 | 146 | _ITM_cxa_begin_catch (void *exc_ptr) _ITM_NOTHROW |
0a35513e | 147 | { |
258c1d07 TR |
148 | // If this exception object has been allocated by this transaction, we |
149 | // discard the undo log entry for the allocation; we are entering phase (3) | |
150 | // now and will handle this exception specially. | |
151 | // Note that this exception cannot have been allocated in a parent | |
152 | // transaction or enclosing nontransactional block because an atomic block | |
153 | // cannot contain just a catch clause but not the associated try clause. | |
154 | // The exception can have been allocated in a nested transaction, in which | |
155 | // case the commit of the nested transaction will have inserted the undo | |
156 | // log entry of the allocation in our undo log. | |
157 | // The exception can also have been allocated in a nested nontransactional | |
158 | // block, but then this transaction cannot abort anymore; functions that | |
159 | // are marked transaction_pure, for example, must not side-step the | |
160 | // transactional exception handling we implement here. | |
161 | gtm_thread *t = gtm_thr (); | |
162 | t->discard_allocation (exc_ptr); | |
163 | // Keep track of the number of unfinished catch handlers. | |
164 | t->cxa_catch_count++; | |
0a35513e AH |
165 | return __cxa_begin_catch (exc_ptr); |
166 | } | |
167 | ||
168 | void | |
169 | _ITM_cxa_end_catch (void) | |
170 | { | |
258c1d07 | 171 | // Keep track of the number of unfinished catch handlers. |
0a35513e AH |
172 | gtm_thr()->cxa_catch_count--; |
173 | __cxa_end_catch (); | |
174 | } | |
175 | ||
258c1d07 TR |
176 | void |
177 | GTM::gtm_thread::init_cpp_exceptions () | |
178 | { | |
179 | // Only save and restore the number of uncaught exceptions if this is | |
180 | // actually used in the program. | |
caa04517 IS |
181 | if ( |
182 | #if HAVE_ELF_STYLE_WEAKREF | |
183 | __cxa_get_globals != NULL && | |
184 | #endif | |
185 | __cxa_get_globals () != 0) | |
258c1d07 TR |
186 | cxa_uncaught_count_ptr = &__cxa_get_globals ()->uncaughtExceptions; |
187 | else | |
188 | cxa_uncaught_count_ptr = 0; | |
189 | } | |
190 | ||
0a35513e AH |
191 | void |
192 | GTM::gtm_thread::revert_cpp_exceptions (gtm_transaction_cp *cp) | |
193 | { | |
194 | if (cp) | |
195 | { | |
258c1d07 TR |
196 | // If rolling back a nested transaction, only clean up incompletely |
197 | // caught exceptions since the last checkpoint. | |
0a35513e AH |
198 | assert (cxa_catch_count >= cp->cxa_catch_count); |
199 | uint32_t catch_count = cxa_catch_count - cp->cxa_catch_count; | |
258c1d07 | 200 | if (catch_count) |
0a35513e | 201 | { |
258c1d07 | 202 | __cxa_tm_cleanup (NULL, NULL, catch_count); |
0a35513e | 203 | cxa_catch_count = cp->cxa_catch_count; |
0a35513e AH |
204 | } |
205 | } | |
206 | else | |
207 | { | |
208 | // Both cxa_catch_count and cxa_unthrown are maximal because EH regions | |
209 | // and transactions are properly nested. | |
258c1d07 | 210 | if (cxa_catch_count) |
0a35513e | 211 | { |
258c1d07 TR |
212 | __cxa_tm_cleanup (NULL, NULL, cxa_catch_count); |
213 | cxa_catch_count = 0; | |
0a35513e AH |
214 | } |
215 | } | |
258c1d07 TR |
216 | // Reset the number of uncaught exceptions. Any allocations for these |
217 | // exceptions have been rolled back already, if necessary. | |
218 | if (cxa_uncaught_count_ptr != 0) | |
219 | *cxa_uncaught_count_ptr = cxa_uncaught_count; | |
220 | // Always reset eh_in_flight because it just contains the argument provided | |
221 | // to _ITM_commitTransactionEH. | |
222 | eh_in_flight = NULL; | |
0a35513e | 223 | } |