145
|
1 /* Copyright (C) 2008-2020 Free Software Foundation, Inc.
|
111
|
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 <stdlib.h>
|
|
26 #include <string.h>
|
|
27 #include <ctype.h>
|
|
28 #include "libitm_i.h"
|
|
29
|
|
30 // The default TM method used when starting a new transaction. Initialized
|
|
31 // in number_of_threads_changed() below.
|
|
32 // Access to this variable is always synchronized with help of the serial
|
|
33 // lock, except one read access that happens in decide_begin_dispatch() before
|
|
34 // a transaction has become active (by acquiring the serial lock in read or
|
|
35 // write mode). The default_dispatch is only changed and initialized in
|
|
36 // serial mode. Transactions stay active when they restart (see beginend.cc),
|
|
37 // thus decide_retry_strategy() can expect default_dispatch to be unmodified.
|
|
38 // See decide_begin_dispatch() for further comments.
|
|
39 static std::atomic<GTM::abi_dispatch*> default_dispatch;
|
|
40 // The default TM method as requested by the user, if any.
|
|
41 static GTM::abi_dispatch* default_dispatch_user = 0;
|
|
42
|
|
43 void
|
|
44 GTM::gtm_thread::decide_retry_strategy (gtm_restart_reason r)
|
|
45 {
|
|
46 struct abi_dispatch *disp = abi_disp ();
|
|
47
|
|
48 this->restart_reason[r]++;
|
|
49 this->restart_total++;
|
|
50
|
|
51 if (r == RESTART_INIT_METHOD_GROUP)
|
|
52 {
|
|
53 // A re-initializations of the method group has been requested. Switch
|
|
54 // to serial mode, initialize, and resume normal operation.
|
|
55 if ((state & STATE_SERIAL) == 0)
|
|
56 {
|
|
57 // We have to eventually re-init the method group. Therefore,
|
|
58 // we cannot just upgrade to a write lock here because this could
|
|
59 // fail forever when other transactions execute in serial mode.
|
|
60 // However, giving up the read lock then means that a change of the
|
|
61 // method group could happen in-between, so check that we're not
|
|
62 // re-initializing without a need.
|
|
63 // ??? Note that we can still re-initialize too often, but avoiding
|
|
64 // that would increase code complexity, which seems unnecessary
|
|
65 // given that re-inits should be very infrequent.
|
|
66 serial_lock.read_unlock(this);
|
|
67 serial_lock.write_lock();
|
|
68 if (disp->get_method_group()
|
|
69 == default_dispatch.load(memory_order_relaxed)
|
|
70 ->get_method_group())
|
|
71 // Still the same method group.
|
|
72 disp->get_method_group()->reinit();
|
|
73 serial_lock.write_unlock();
|
|
74 // Also, we're making the transaction inactive, so when we become
|
|
75 // active again, some other thread might have changed the default
|
|
76 // dispatch, so we run the same code as for the first execution
|
|
77 // attempt.
|
|
78 disp = decide_begin_dispatch(prop);
|
|
79 set_abi_disp(disp);
|
|
80 }
|
|
81 else
|
|
82 // We are a serial transaction already, which makes things simple.
|
|
83 disp->get_method_group()->reinit();
|
|
84
|
|
85 return;
|
|
86 }
|
|
87
|
|
88 bool retry_irr = (r == RESTART_SERIAL_IRR);
|
|
89 bool retry_serial = (retry_irr || this->restart_total > 100);
|
|
90
|
|
91 // We assume closed nesting to be infrequently required, so just use
|
|
92 // dispatch_serial (with undo logging) if required.
|
|
93 if (r == RESTART_CLOSED_NESTING)
|
|
94 retry_serial = true;
|
|
95
|
|
96 if (retry_serial)
|
|
97 {
|
|
98 // In serialirr_mode we can succeed with the upgrade to
|
|
99 // write-lock but fail the trycommit. In any case, if the
|
|
100 // write lock is not yet held, grab it. Don't do this with
|
|
101 // an upgrade, since we've no need to preserve the state we
|
|
102 // acquired with the read.
|
|
103 // Note that we will be restarting with either dispatch_serial or
|
|
104 // dispatch_serialirr, which are compatible with all TM methods; if
|
|
105 // we would retry with a different method, we would have to first check
|
|
106 // whether the default dispatch or the method group have changed. Also,
|
|
107 // the caller must have rolled back the previous transaction, so we
|
|
108 // don't have to worry about things such as privatization.
|
|
109 if ((this->state & STATE_SERIAL) == 0)
|
|
110 {
|
|
111 this->state |= STATE_SERIAL;
|
|
112 serial_lock.read_unlock (this);
|
|
113 serial_lock.write_lock ();
|
|
114 }
|
|
115
|
|
116 // We can retry with dispatch_serialirr if the transaction
|
|
117 // doesn't contain an abort and if we don't need closed nesting.
|
|
118 if ((this->prop & pr_hasNoAbort) && (r != RESTART_CLOSED_NESTING))
|
|
119 retry_irr = true;
|
|
120 }
|
|
121
|
|
122 // Note that we can just use serial mode here without having to switch
|
|
123 // TM method sets because serial mode is compatible with all of them.
|
|
124 if (retry_irr)
|
|
125 {
|
|
126 this->state = (STATE_SERIAL | STATE_IRREVOCABLE);
|
|
127 disp = dispatch_serialirr ();
|
|
128 set_abi_disp (disp);
|
|
129 }
|
|
130 else if (retry_serial)
|
|
131 {
|
|
132 disp = dispatch_serial();
|
|
133 set_abi_disp (disp);
|
|
134 }
|
|
135 }
|
|
136
|
|
137
|
|
138 // Decides which TM method should be used on the first attempt to run this
|
|
139 // transaction. Acquires the serial lock and sets transaction state
|
|
140 // according to the chosen TM method.
|
|
141 GTM::abi_dispatch*
|
|
142 GTM::gtm_thread::decide_begin_dispatch (uint32_t prop)
|
|
143 {
|
|
144 abi_dispatch* dd;
|
|
145 // TODO Pay more attention to prop flags (eg, *omitted) when selecting
|
|
146 // dispatch.
|
|
147 // ??? We go irrevocable eagerly here, which is not always good for
|
|
148 // performance. Don't do this?
|
|
149 if ((prop & pr_doesGoIrrevocable) || !(prop & pr_instrumentedCode))
|
|
150 dd = dispatch_serialirr();
|
|
151
|
|
152 else
|
|
153 {
|
|
154 // Load the default dispatch. We're not an active transaction and so it
|
|
155 // can change concurrently but will still be some valid dispatch.
|
|
156 // Relaxed memory order is okay because we expect each dispatch to be
|
|
157 // constructed properly already (at least that its closed_nesting() and
|
|
158 // closed_nesting_alternatives() will return sensible values). It is
|
|
159 // harmless if we incorrectly chose the serial or serialirr methods, and
|
|
160 // for all other methods we will acquire the serial lock in read mode
|
|
161 // and load the default dispatch again.
|
|
162 abi_dispatch* dd_orig = default_dispatch.load(memory_order_relaxed);
|
|
163 dd = dd_orig;
|
|
164
|
|
165 // If we might need closed nesting and the default dispatch has an
|
|
166 // alternative that supports closed nesting, use it.
|
|
167 // ??? We could choose another TM method that we know supports closed
|
|
168 // nesting but isn't the default (e.g., dispatch_serial()). However, we
|
|
169 // assume that aborts that need closed nesting are infrequent, so don't
|
|
170 // choose a non-default method until we have to actually restart the
|
|
171 // transaction.
|
|
172 if (!(prop & pr_hasNoAbort) && !dd->closed_nesting()
|
|
173 && dd->closed_nesting_alternative())
|
|
174 dd = dd->closed_nesting_alternative();
|
|
175
|
|
176 if (!(dd->requires_serial() & STATE_SERIAL))
|
|
177 {
|
|
178 // The current dispatch is supposedly a non-serial one. Become an
|
|
179 // active transaction and verify this. Relaxed memory order is fine
|
|
180 // because the serial lock itself will have established
|
|
181 // happens-before for any change to the selected dispatch.
|
|
182 serial_lock.read_lock (this);
|
|
183 if (default_dispatch.load(memory_order_relaxed) == dd_orig)
|
|
184 return dd;
|
|
185
|
|
186 // If we raced with a concurrent modification of default_dispatch,
|
|
187 // just fall back to serialirr. The dispatch choice might not be
|
|
188 // up-to-date anymore, but this is harmless.
|
|
189 serial_lock.read_unlock (this);
|
|
190 dd = dispatch_serialirr();
|
|
191 }
|
|
192 }
|
|
193
|
|
194 // We are some kind of serial transaction.
|
|
195 serial_lock.write_lock();
|
|
196 state = dd->requires_serial();
|
|
197 return dd;
|
|
198 }
|
|
199
|
|
200
|
|
201 void
|
|
202 GTM::gtm_thread::set_default_dispatch(GTM::abi_dispatch* disp)
|
|
203 {
|
|
204 abi_dispatch* dd = default_dispatch.load(memory_order_relaxed);
|
|
205 if (dd == disp)
|
|
206 return;
|
|
207 if (dd)
|
|
208 {
|
|
209 // If we are switching method groups, initialize and shut down properly.
|
|
210 if (dd->get_method_group() != disp->get_method_group())
|
|
211 {
|
|
212 dd->get_method_group()->fini();
|
|
213 disp->get_method_group()->init();
|
|
214 }
|
|
215 }
|
|
216 else
|
|
217 disp->get_method_group()->init();
|
|
218 default_dispatch.store(disp, memory_order_relaxed);
|
|
219 }
|
|
220
|
|
221
|
|
222 static GTM::abi_dispatch*
|
|
223 parse_default_method()
|
|
224 {
|
|
225 const char *env = getenv("ITM_DEFAULT_METHOD");
|
|
226 GTM::abi_dispatch* disp = 0;
|
|
227 if (env == NULL)
|
|
228 return 0;
|
|
229
|
|
230 while (isspace((unsigned char) *env))
|
|
231 ++env;
|
|
232 if (strncmp(env, "serialirr_onwrite", 17) == 0)
|
|
233 {
|
|
234 disp = GTM::dispatch_serialirr_onwrite();
|
|
235 env += 17;
|
|
236 }
|
|
237 else if (strncmp(env, "serialirr", 9) == 0)
|
|
238 {
|
|
239 disp = GTM::dispatch_serialirr();
|
|
240 env += 9;
|
|
241 }
|
|
242 else if (strncmp(env, "serial", 6) == 0)
|
|
243 {
|
|
244 disp = GTM::dispatch_serial();
|
|
245 env += 6;
|
|
246 }
|
|
247 else if (strncmp(env, "gl_wt", 5) == 0)
|
|
248 {
|
|
249 disp = GTM::dispatch_gl_wt();
|
|
250 env += 5;
|
|
251 }
|
|
252 else if (strncmp(env, "ml_wt", 5) == 0)
|
|
253 {
|
|
254 disp = GTM::dispatch_ml_wt();
|
|
255 env += 5;
|
|
256 }
|
|
257 else if (strncmp(env, "htm", 3) == 0)
|
|
258 {
|
|
259 disp = GTM::dispatch_htm();
|
|
260 env += 3;
|
|
261 }
|
|
262 else
|
|
263 goto unknown;
|
|
264
|
|
265 while (isspace((unsigned char) *env))
|
|
266 ++env;
|
|
267 if (*env == '\0')
|
|
268 return disp;
|
|
269
|
|
270 unknown:
|
|
271 GTM::GTM_error("Unknown TM method in environment variable "
|
|
272 "ITM_DEFAULT_METHOD\n");
|
|
273 return 0;
|
|
274 }
|
|
275
|
|
276 // Gets notifications when the number of registered threads changes. This is
|
|
277 // used to initialize the method set choice and trigger straightforward choice
|
|
278 // adaption.
|
|
279 // This must be called only by serial threads.
|
|
280 void
|
|
281 GTM::gtm_thread::number_of_threads_changed(unsigned previous, unsigned now)
|
|
282 {
|
|
283 if (previous == 0)
|
|
284 {
|
|
285 // No registered threads before, so initialize.
|
|
286 static bool initialized = false;
|
|
287 if (!initialized)
|
|
288 {
|
|
289 initialized = true;
|
|
290 // Check for user preferences here.
|
|
291 default_dispatch = 0;
|
|
292 default_dispatch_user = parse_default_method();
|
|
293 }
|
|
294 }
|
|
295 else if (now == 0)
|
|
296 {
|
|
297 // No registered threads anymore. The dispatch based on serial mode do
|
|
298 // not have any global state, so this effectively shuts down properly.
|
|
299 set_default_dispatch(dispatch_serialirr());
|
|
300 }
|
|
301
|
|
302 if (now == 1)
|
|
303 {
|
|
304 // Only one thread, so use a serializing method.
|
|
305 // ??? If we don't have a fast serial mode implementation, it might be
|
|
306 // better to use the global lock method set here.
|
|
307 if (default_dispatch_user && default_dispatch_user->supports(now))
|
|
308 set_default_dispatch(default_dispatch_user);
|
|
309 else
|
|
310 set_default_dispatch(dispatch_serialirr());
|
|
311 }
|
|
312 else if (now > 1 && previous <= 1)
|
|
313 {
|
|
314 // More than one thread, use the default method.
|
|
315 if (default_dispatch_user && default_dispatch_user->supports(now))
|
|
316 set_default_dispatch(default_dispatch_user);
|
|
317 else
|
|
318 {
|
|
319 // If HTM is available, use it by default with serial mode as
|
|
320 // fallback. Otherwise, use ml_wt because it probably scales best.
|
|
321 abi_dispatch* a;
|
|
322 #ifdef USE_HTM_FASTPATH
|
|
323 if (htm_available())
|
|
324 a = dispatch_htm();
|
|
325 else
|
|
326 #endif
|
|
327 a = dispatch_ml_wt();
|
|
328 if (a->supports(now))
|
|
329 set_default_dispatch(a);
|
|
330 else
|
|
331 // Serial-irrevocable mode always works.
|
|
332 set_default_dispatch(dispatch_serialirr());
|
|
333 }
|
|
334 }
|
|
335 }
|