diff options
author | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
---|---|---|
committer | Ben Cheng <bccheng@google.com> | 2014-03-25 22:37:19 -0700 |
commit | 1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch) | |
tree | c607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/libitm/retry.cc | |
parent | 283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff) | |
download | toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.gz toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.bz2 toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.zip |
Initial checkin of GCC 4.9.0 from trunk (r208799).
Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba
Diffstat (limited to 'gcc-4.9/libitm/retry.cc')
-rw-r--r-- | gcc-4.9/libitm/retry.cc | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/gcc-4.9/libitm/retry.cc b/gcc-4.9/libitm/retry.cc new file mode 100644 index 000000000..cec864bd4 --- /dev/null +++ b/gcc-4.9/libitm/retry.cc @@ -0,0 +1,335 @@ +/* Copyright (C) 2008-2014 Free Software Foundation, Inc. + Contributed by Richard Henderson <rth@redhat.com>. + + This file is part of the GNU Transactional Memory Library (libitm). + + Libitm is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Libitm is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "libitm_i.h" + +// The default TM method used when starting a new transaction. Initialized +// in number_of_threads_changed() below. +// Access to this variable is always synchronized with help of the serial +// lock, except one read access that happens in decide_begin_dispatch() before +// a transaction has become active (by acquiring the serial lock in read or +// write mode). The default_dispatch is only changed and initialized in +// serial mode. Transactions stay active when they restart (see beginend.cc), +// thus decide_retry_strategy() can expect default_dispatch to be unmodified. +// See decide_begin_dispatch() for further comments. +static std::atomic<GTM::abi_dispatch*> default_dispatch; +// The default TM method as requested by the user, if any. +static GTM::abi_dispatch* default_dispatch_user = 0; + +void +GTM::gtm_thread::decide_retry_strategy (gtm_restart_reason r) +{ + struct abi_dispatch *disp = abi_disp (); + + this->restart_reason[r]++; + this->restart_total++; + + if (r == RESTART_INIT_METHOD_GROUP) + { + // A re-initializations of the method group has been requested. Switch + // to serial mode, initialize, and resume normal operation. + if ((state & STATE_SERIAL) == 0) + { + // We have to eventually re-init the method group. Therefore, + // we cannot just upgrade to a write lock here because this could + // fail forever when other transactions execute in serial mode. + // However, giving up the read lock then means that a change of the + // method group could happen in-between, so check that we're not + // re-initializing without a need. + // ??? Note that we can still re-initialize too often, but avoiding + // that would increase code complexity, which seems unnecessary + // given that re-inits should be very infrequent. + serial_lock.read_unlock(this); + serial_lock.write_lock(); + if (disp->get_method_group() + == default_dispatch.load(memory_order_relaxed) + ->get_method_group()) + // Still the same method group. + disp->get_method_group()->reinit(); + serial_lock.write_unlock(); + // Also, we're making the transaction inactive, so when we become + // active again, some other thread might have changed the default + // dispatch, so we run the same code as for the first execution + // attempt. + disp = decide_begin_dispatch(prop); + set_abi_disp(disp); + } + else + // We are a serial transaction already, which makes things simple. + disp->get_method_group()->reinit(); + + return; + } + + bool retry_irr = (r == RESTART_SERIAL_IRR); + bool retry_serial = (retry_irr || this->restart_total > 100); + + // We assume closed nesting to be infrequently required, so just use + // dispatch_serial (with undo logging) if required. + if (r == RESTART_CLOSED_NESTING) + retry_serial = true; + + if (retry_serial) + { + // In serialirr_mode we can succeed with the upgrade to + // write-lock but fail the trycommit. In any case, if the + // write lock is not yet held, grab it. Don't do this with + // an upgrade, since we've no need to preserve the state we + // acquired with the read. + // Note that we will be restarting with either dispatch_serial or + // dispatch_serialirr, which are compatible with all TM methods; if + // we would retry with a different method, we would have to first check + // whether the default dispatch or the method group have changed. Also, + // the caller must have rolled back the previous transaction, so we + // don't have to worry about things such as privatization. + if ((this->state & STATE_SERIAL) == 0) + { + this->state |= STATE_SERIAL; + serial_lock.read_unlock (this); + serial_lock.write_lock (); + } + + // We can retry with dispatch_serialirr if the transaction + // doesn't contain an abort and if we don't need closed nesting. + if ((this->prop & pr_hasNoAbort) && (r != RESTART_CLOSED_NESTING)) + retry_irr = true; + } + + // Note that we can just use serial mode here without having to switch + // TM method sets because serial mode is compatible with all of them. + if (retry_irr) + { + this->state = (STATE_SERIAL | STATE_IRREVOCABLE); + disp = dispatch_serialirr (); + set_abi_disp (disp); + } + else if (retry_serial) + { + disp = dispatch_serial(); + set_abi_disp (disp); + } +} + + +// Decides which TM method should be used on the first attempt to run this +// transaction. Acquires the serial lock and sets transaction state +// according to the chosen TM method. +GTM::abi_dispatch* +GTM::gtm_thread::decide_begin_dispatch (uint32_t prop) +{ + abi_dispatch* dd; + // TODO Pay more attention to prop flags (eg, *omitted) when selecting + // dispatch. + // ??? We go irrevocable eagerly here, which is not always good for + // performance. Don't do this? + if ((prop & pr_doesGoIrrevocable) || !(prop & pr_instrumentedCode)) + dd = dispatch_serialirr(); + + else + { + // Load the default dispatch. We're not an active transaction and so it + // can change concurrently but will still be some valid dispatch. + // Relaxed memory order is okay because we expect each dispatch to be + // constructed properly already (at least that its closed_nesting() and + // closed_nesting_alternatives() will return sensible values). It is + // harmless if we incorrectly chose the serial or serialirr methods, and + // for all other methods we will acquire the serial lock in read mode + // and load the default dispatch again. + abi_dispatch* dd_orig = default_dispatch.load(memory_order_relaxed); + dd = dd_orig; + + // If we might need closed nesting and the default dispatch has an + // alternative that supports closed nesting, use it. + // ??? We could choose another TM method that we know supports closed + // nesting but isn't the default (e.g., dispatch_serial()). However, we + // assume that aborts that need closed nesting are infrequent, so don't + // choose a non-default method until we have to actually restart the + // transaction. + if (!(prop & pr_hasNoAbort) && !dd->closed_nesting() + && dd->closed_nesting_alternative()) + dd = dd->closed_nesting_alternative(); + + if (!(dd->requires_serial() & STATE_SERIAL)) + { + // The current dispatch is supposedly a non-serial one. Become an + // active transaction and verify this. Relaxed memory order is fine + // because the serial lock itself will have established + // happens-before for any change to the selected dispatch. + serial_lock.read_lock (this); + if (default_dispatch.load(memory_order_relaxed) == dd_orig) + return dd; + + // If we raced with a concurrent modification of default_dispatch, + // just fall back to serialirr. The dispatch choice might not be + // up-to-date anymore, but this is harmless. + serial_lock.read_unlock (this); + dd = dispatch_serialirr(); + } + } + + // We are some kind of serial transaction. + serial_lock.write_lock(); + state = dd->requires_serial(); + return dd; +} + + +void +GTM::gtm_thread::set_default_dispatch(GTM::abi_dispatch* disp) +{ + abi_dispatch* dd = default_dispatch.load(memory_order_relaxed); + if (dd == disp) + return; + if (dd) + { + // If we are switching method groups, initialize and shut down properly. + if (dd->get_method_group() != disp->get_method_group()) + { + dd->get_method_group()->fini(); + disp->get_method_group()->init(); + } + } + else + disp->get_method_group()->init(); + default_dispatch.store(disp, memory_order_relaxed); +} + + +static GTM::abi_dispatch* +parse_default_method() +{ + const char *env = getenv("ITM_DEFAULT_METHOD"); + GTM::abi_dispatch* disp = 0; + if (env == NULL) + return 0; + + while (isspace((unsigned char) *env)) + ++env; + if (strncmp(env, "serialirr_onwrite", 17) == 0) + { + disp = GTM::dispatch_serialirr_onwrite(); + env += 17; + } + else if (strncmp(env, "serialirr", 9) == 0) + { + disp = GTM::dispatch_serialirr(); + env += 9; + } + else if (strncmp(env, "serial", 6) == 0) + { + disp = GTM::dispatch_serial(); + env += 6; + } + else if (strncmp(env, "gl_wt", 5) == 0) + { + disp = GTM::dispatch_gl_wt(); + env += 5; + } + else if (strncmp(env, "ml_wt", 5) == 0) + { + disp = GTM::dispatch_ml_wt(); + env += 5; + } + else if (strncmp(env, "htm", 3) == 0) + { + disp = GTM::dispatch_htm(); + env += 3; + } + else + goto unknown; + + while (isspace((unsigned char) *env)) + ++env; + if (*env == '\0') + return disp; + + unknown: + GTM::GTM_error("Unknown TM method in environment variable " + "ITM_DEFAULT_METHOD\n"); + return 0; +} + +// Gets notifications when the number of registered threads changes. This is +// used to initialize the method set choice and trigger straightforward choice +// adaption. +// This must be called only by serial threads. +void +GTM::gtm_thread::number_of_threads_changed(unsigned previous, unsigned now) +{ + if (previous == 0) + { + // No registered threads before, so initialize. + static bool initialized = false; + if (!initialized) + { + initialized = true; + // Check for user preferences here. + default_dispatch = 0; + default_dispatch_user = parse_default_method(); + } + } + else if (now == 0) + { + // No registered threads anymore. The dispatch based on serial mode do + // not have any global state, so this effectively shuts down properly. + set_default_dispatch(dispatch_serialirr()); + } + + if (now == 1) + { + // Only one thread, so use a serializing method. + // ??? If we don't have a fast serial mode implementation, it might be + // better to use the global lock method set here. + if (default_dispatch_user && default_dispatch_user->supports(now)) + set_default_dispatch(default_dispatch_user); + else + set_default_dispatch(dispatch_serialirr()); + } + else if (now > 1 && previous <= 1) + { + // More than one thread, use the default method. + if (default_dispatch_user && default_dispatch_user->supports(now)) + set_default_dispatch(default_dispatch_user); + else + { + // If HTM is available, use it by default with serial mode as + // fallback. Otherwise, use ml_wt because it probably scales best. + abi_dispatch* a; +#ifdef USE_HTM_FASTPATH + if (htm_available()) + a = dispatch_htm(); + else +#endif + a = dispatch_ml_wt(); + if (a->supports(now)) + set_default_dispatch(a); + else + // Serial-irrevocable mode always works. + set_default_dispatch(dispatch_serialirr()); + } + } +} |