annotate libitm/retry.cc @ 158:494b0b89df80 default tip

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