aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/libitm/retry.cc
diff options
context:
space:
mode:
authorBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
committerBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
commit1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch)
treec607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/libitm/retry.cc
parent283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff)
downloadtoolchain_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.cc335
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());
+ }
+ }
+}