| <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
| <html> |
| <!-- Copyright (C) 2011-2014 Free Software Foundation, Inc. |
| |
| Permission is granted to copy, distribute and/or modify this document |
| under the terms of the GNU Free Documentation License, Version 1.2 or |
| any later version published by the Free Software Foundation; with no |
| Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. |
| A copy of the license is included in the section entitled "GNU |
| Free Documentation License". --> |
| <!-- Created by GNU Texinfo 5.2, http://www.gnu.org/software/texinfo/ --> |
| <head> |
| <title>GNU libitm: Internals</title> |
| |
| <meta name="description" content="GNU libitm: Internals"> |
| <meta name="keywords" content="GNU libitm: Internals"> |
| <meta name="resource-type" content="document"> |
| <meta name="distribution" content="global"> |
| <meta name="Generator" content="makeinfo"> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| <link href="index.html#Top" rel="start" title="Top"> |
| <link href="Library-Index.html#Library-Index" rel="index" title="Library Index"> |
| <link href="index.html#SEC_Contents" rel="contents" title="Table of Contents"> |
| <link href="index.html#Top" rel="up" title="Top"> |
| <link href="GNU-Free-Documentation-License.html#GNU-Free-Documentation-License" rel="next" title="GNU Free Documentation License"> |
| <link href="The-libitm-ABI.html#The-libitm-ABI" rel="prev" title="The libitm ABI"> |
| <style type="text/css"> |
| <!-- |
| a.summary-letter {text-decoration: none} |
| blockquote.smallquotation {font-size: smaller} |
| div.display {margin-left: 3.2em} |
| div.example {margin-left: 3.2em} |
| div.indentedblock {margin-left: 3.2em} |
| div.lisp {margin-left: 3.2em} |
| div.smalldisplay {margin-left: 3.2em} |
| div.smallexample {margin-left: 3.2em} |
| div.smallindentedblock {margin-left: 3.2em; font-size: smaller} |
| div.smalllisp {margin-left: 3.2em} |
| kbd {font-style:oblique} |
| pre.display {font-family: inherit} |
| pre.format {font-family: inherit} |
| pre.menu-comment {font-family: serif} |
| pre.menu-preformatted {font-family: serif} |
| pre.smalldisplay {font-family: inherit; font-size: smaller} |
| pre.smallexample {font-size: smaller} |
| pre.smallformat {font-family: inherit; font-size: smaller} |
| pre.smalllisp {font-size: smaller} |
| span.nocodebreak {white-space:nowrap} |
| span.nolinebreak {white-space:nowrap} |
| span.roman {font-family:serif; font-weight:normal} |
| span.sansserif {font-family:sans-serif; font-weight:normal} |
| ul.no-bullet {list-style: none} |
| --> |
| </style> |
| |
| |
| </head> |
| |
| <body lang="en" bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#800080" alink="#FF0000"> |
| <a name="Internals"></a> |
| <div class="header"> |
| <p> |
| Next: <a href="GNU-Free-Documentation-License.html#GNU-Free-Documentation-License" accesskey="n" rel="next">GNU Free Documentation License</a>, Previous: <a href="The-libitm-ABI.html#The-libitm-ABI" accesskey="p" rel="prev">The libitm ABI</a>, Up: <a href="index.html#Top" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Library-Index.html#Library-Index" title="Index" rel="index">Index</a>]</p> |
| </div> |
| <hr> |
| <a name="Internals-1"></a> |
| <h2 class="chapter">4 Internals</h2> |
| |
| <a name="TM-methods-and-method-groups"></a> |
| <h3 class="section">4.1 TM methods and method groups</h3> |
| |
| <p>libitm supports several ways of synchronizing transactions with each other. |
| These TM methods (or TM algorithms) are implemented in the form of |
| subclasses of <code>abi_dispatch</code>, which provide methods for |
| transactional loads and stores as well as callbacks for rollback and commit. |
| All methods that are compatible with each other (i.e., that let concurrently |
| running transactions still synchronize correctly even if different methods |
| are used) belong to the same TM method group. Pointers to TM methods can be |
| obtained using the factory methods prefixed with <code>dispatch_</code> in |
| <samp>libitm_i.h</samp>. There are two special methods, <code>dispatch_serial</code> and |
| <code>dispatch_serialirr</code>, that are compatible with all methods because they |
| run transactions completely in serial mode. |
| </p> |
| <a name="TM-method-life-cycle"></a> |
| <h4 class="subsection">4.1.1 TM method life cycle</h4> |
| |
| <p>The state of TM methods does not change after construction, but they do alter |
| the state of transactions that use this method. However, because |
| per-transaction data gets used by several methods, <code>gtm_thread</code> is |
| responsible for setting an initial state that is useful for all methods. |
| After that, methods are responsible for resetting/clearing this state on each |
| rollback or commit (of outermost transactions), so that the transaction |
| executed next is not affected by the previous transaction. |
| </p> |
| <p>There is also global state associated with each method group, which is |
| initialized and shut down (<code>method_group::init()</code> and <code>fini()</code>) |
| when switching between method groups (see <samp>retry.cc</samp>). |
| </p> |
| <a name="Selecting-the-default-method"></a> |
| <h4 class="subsection">4.1.2 Selecting the default method</h4> |
| |
| <p>The default method that libitm uses for freshly started transactions (but |
| not necessarily for restarted transactions) can be set via an environment |
| variable (<code>ITM_DEFAULT_METHOD</code>), whose value should be equal to the name |
| of one of the factory methods returning abi_dispatch subclasses but without |
| the "dispatch_" prefix (e.g., "serialirr" instead of |
| <code>GTM::dispatch_serialirr()</code>). |
| </p> |
| <p>Note that this environment variable is only a hint for libitm and might not |
| be supported in the future. |
| </p> |
| |
| <a name="Nesting_003a-flat-vs_002e-closed"></a> |
| <h3 class="section">4.2 Nesting: flat vs. closed</h3> |
| |
| <p>We support two different kinds of nesting of transactions. In the case of |
| <em>flat nesting</em>, the nesting structure is flattened and all nested |
| transactions are subsumed by the enclosing transaction. In contrast, |
| with <em>closed nesting</em>, nested transactions that have not yet committed |
| can be rolled back separately from the enclosing transactions; when they |
| commit, they are subsumed by the enclosing transaction, and their effects |
| will be finally committed when the outermost transaction commits. |
| <em>Open nesting</em> (where nested transactions can commit independently of the |
| enclosing transactions) are not supported. |
| </p> |
| <p>Flat nesting is the default nesting mode, but closed nesting is supported and |
| used when transactions contain user-controlled aborts |
| (<code>__transaction_cancel</code> statements). We assume that user-controlled |
| aborts are rare in typical code and used mostly in exceptional situations. |
| Thus, it makes more sense to use flat nesting by default to avoid the |
| performance overhead of the additional checkpoints required for closed |
| nesting. User-controlled aborts will correctly abort the innermost enclosing |
| transaction, whereas the whole (i.e., outermost) transaction will be restarted |
| otherwise (e.g., when a transaction encounters data conflicts during |
| optimistic execution). |
| </p> |
| |
| <a name="Locking-conventions"></a> |
| <h3 class="section">4.3 Locking conventions</h3> |
| |
| <p>This section documents the locking scheme and rules for all uses of locking |
| in libitm. We have to support serial(-irrevocable) mode, which is implemented |
| using a global lock as explained next (called the <em>serial lock</em>). To |
| simplify the overall design, we use the same lock as catch-all locking |
| mechanism for other infrequent tasks such as (de)registering clone tables or |
| threads. Besides the serial lock, there are <em>per-method-group locks</em> that |
| are managed by specific method groups (i.e., groups of similar TM concurrency |
| control algorithms), and lock-like constructs for quiescence-based operations |
| such as ensuring privatization safety. |
| </p> |
| <p>Thus, the actions that participate in the libitm-internal locking are either |
| <em>active transactions</em> that do not run in serial mode, <em>serial |
| transactions</em> (which (are about to) run in serial mode), and management tasks |
| that do not execute within a transaction but have acquired the serial mode |
| like a serial transaction would do (e.g., to be able to register threads with |
| libitm). Transactions become active as soon as they have successfully used the |
| serial lock to announce this globally (see <a href="#serial_002dlock_002dimpl">Serial lock |
| implementation</a>). Likewise, transactions become serial transactions as soon as |
| they have acquired the exclusive rights provided by the serial lock (i.e., |
| serial mode, which also means that there are no other concurrent active or |
| serial transactions). Note that active transactions can become serial |
| transactions when they enter serial mode during the runtime of the |
| transaction. |
| </p> |
| <a name="State_002dto_002dlock-mapping"></a> |
| <h4 class="subsection">4.3.1 State-to-lock mapping</h4> |
| |
| <p>Application data is protected by the serial lock if there is a serial |
| transaction and no concurrently running active transaction (i.e., non-serial). |
| Otherwise, application data is protected by the currently selected method |
| group, which might use per-method-group locks or other mechanisms. Also note |
| that application data that is about to be privatized might not be allowed to be |
| accessed by nontransactional code until privatization safety has been ensured; |
| the details of this are handled by the current method group. |
| </p> |
| <p>libitm-internal state is either protected by the serial lock or accessed |
| through custom concurrent code. The latter applies to the public/shared part |
| of a transaction object and most typical method-group-specific state. |
| </p> |
| <p>The former category (protected by the serial lock) includes: |
| </p><ul> |
| <li> The list of active threads that have used transactions. |
| </li><li> The tables that map functions to their transactional clones. |
| </li><li> The current selection of which method group to use. |
| </li><li> Some method-group-specific data, or invariants of this data. For example, |
| resetting a method group to its initial state is handled by switching to the |
| same method group, so the serial lock protects such resetting as well. |
| </li></ul> |
| <p>In general, such state is immutable whenever there exists an active |
| (non-serial) transaction. If there is no active transaction, a serial |
| transaction (or a thread that is not currently executing a transaction but has |
| acquired the serial lock) is allowed to modify this state (but must of course |
| be careful to not surprise the current method group’s implementation with such |
| modifications). |
| </p> |
| <a name="Lock-acquisition-order"></a> |
| <h4 class="subsection">4.3.2 Lock acquisition order</h4> |
| |
| <p>To prevent deadlocks, locks acquisition must happen in a globally agreed-upon |
| order. Note that this applies to other forms of blocking too, but does not |
| necessarily apply to lock acquisitions that do not block (e.g., trylock() |
| calls that do not get retried forever). Note that serial transactions are |
| never return back to active transactions until the transaction has committed. |
| Likewise, active transactions stay active until they have committed. |
| Per-method-group locks are typically also not released before commit. |
| </p> |
| <p>Lock acquisition / blocking rules: |
| </p><ul> |
| <li> Transactions must become active or serial before they are allowed to |
| use method-group-specific locks or blocking (i.e., the serial lock must be |
| acquired before those other locks, either in serial or nonserial mode). |
| |
| </li><li> Any number of threads that do not currently run active transactions can |
| block while trying to get the serial lock in exclusive mode. Note that active |
| transactions must not block when trying to upgrade to serial mode unless there |
| is no other transaction that is trying that (the latter is ensured by the |
| serial lock implementation. |
| |
| </li><li> Method groups must prevent deadlocks on their locks. In particular, they |
| must also be prepared for another active transaction that has acquired |
| method-group-specific locks but is blocked during an attempt to upgrade to |
| being a serial transaction. See below for details. |
| |
| </li><li> Serial transactions can acquire method-group-specific locks because there |
| will be no other active nor serial transaction. |
| |
| </li></ul> |
| |
| <p>There is no single rule for per-method-group blocking because this depends on |
| when a TM method might acquire locks. If no active transaction can upgrade to |
| being a serial transaction after it has acquired per-method-group locks (e.g., |
| when those locks are only acquired during an attempt to commit), then the TM |
| method does not need to consider a potential deadlock due to serial mode. |
| </p> |
| <p>If there can be upgrades to serial mode after the acquisition of |
| per-method-group locks, then TM methods need to avoid those deadlocks: |
| </p><ul> |
| <li> When upgrading to a serial transaction, after acquiring exclusive rights |
| to the serial lock but before waiting for concurrent active transactions to |
| finish (see <a href="#serial_002dlock_002dimpl">Serial lock implementation</a> for details), |
| we have to wake up all active transactions waiting on the upgrader’s |
| per-method-group locks. |
| </li><li> Active transactions blocking on per-method-group locks need to check the |
| serial lock and abort if there is a pending serial transaction. |
| </li><li> Lost wake-ups have to be prevented (e.g., by changing a bit in each |
| per-method-group lock before doing the wake-up, and only blocking on this lock |
| using a futex if this bit is not group). |
| </li></ul> |
| |
| <p><strong>TODO</strong>: Can reuse serial lock for gl-*? And if we can, does it make |
| sense to introduce further complexity in the serial lock? For gl-*, we can |
| really only avoid an abort if we do -wb and -vbv. |
| </p> |
| |
| <a name="Serial-lock-implementation"></a> |
| <h4 class="subsection">4.3.3 Serial lock implementation</h4> |
| <a name="serial_002dlock_002dimpl"></a> |
| <p>The serial lock implementation is optimized towards assuming that serial |
| transactions are infrequent and not the common case. However, the performance |
| of entering serial mode can matter because when only few transactions are run |
| concurrently or if there are few threads, then it can be efficient to run |
| transactions serially. |
| </p> |
| <p>The serial lock is similar to a multi-reader-single-writer lock in that there |
| can be several active transactions but only one serial transaction. However, |
| we do want to avoid contention (in the lock implementation) between active |
| transactions, so we split up the reader side of the lock into per-transaction |
| flags that are true iff the transaction is active. The exclusive writer side |
| remains a shared single flag, which is acquired using a CAS, for example. |
| On the fast-path, the serial lock then works similar to Dekker’s algorithm but |
| with several reader flags that a serial transaction would have to check. |
| A serial transaction thus requires a list of all threads with potentially |
| active transactions; we can use the serial lock itself to protect this list |
| (i.e., only threads that have acquired the serial lock can modify this list). |
| </p> |
| <p>We want starvation-freedom for the serial lock to allow for using it to ensure |
| progress for potentially starved transactions (see <a href="#progress_002dguarantees">Progress Guarantees</a> for details). However, this is currently not enforced by |
| the implementation of the serial lock. |
| </p> |
| <p>Here is pseudo-code for the read/write fast paths of acquiring the serial |
| lock (read-to-write upgrade is similar to write_lock: |
| </p><div class="example"> |
| <pre class="example">// read_lock: |
| tx->shared_state |= active; |
| __sync_synchronize(); // or STLD membar, or C++0x seq-cst fence |
| while (!serial_lock.exclusive) |
| if (spinning_for_too_long) goto slowpath; |
| |
| // write_lock: |
| if (CAS(&serial_lock.exclusive, 0, this) != 0) |
| goto slowpath; // writer-writer contention |
| // need a membar here, but CAS already has full membar semantics |
| bool need_blocking = false; |
| for (t: all txns) |
| { |
| for (;t->shared_state & active;) |
| if (spinning_for_too_long) { need_blocking = true; break; } |
| } |
| if (need_blocking) goto slowpath; |
| </pre></div> |
| |
| <p>Releasing a lock in this spin-lock version then just consists of resetting |
| <code>tx->shared_state</code> to inactive or clearing <code>serial_lock.exclusive</code>. |
| </p> |
| <p>However, we can’t rely on a pure spinlock because we need to get the OS |
| involved at some time (e.g., when there are more threads than CPUs to run on). |
| Therefore, the real implementation falls back to a blocking slow path, either |
| based on pthread mutexes or Linux futexes. |
| </p> |
| |
| <a name="Reentrancy"></a> |
| <h4 class="subsection">4.3.4 Reentrancy</h4> |
| |
| <p>libitm has to consider the following cases of reentrancy: |
| </p><ul> |
| <li> Transaction calls unsafe code that starts a new transaction: The outer |
| transaction will become a serial transaction before executing unsafe code. |
| Therefore, nesting within serial transactions must work, even if the nested |
| transaction is called from within uninstrumented code. |
| |
| </li><li> Transaction calls either a transactional wrapper or safe code, which in |
| turn starts a new transaction: It is not yet defined in the specification |
| whether this is allowed. Thus, it is undefined whether libitm supports this. |
| |
| </li><li> Code that starts new transactions might be called from within any part |
| of libitm: This kind of reentrancy would likely be rather complex and can |
| probably be avoided. Therefore, it is not supported. |
| |
| </li></ul> |
| |
| <a name="Privatization-safety"></a> |
| <h4 class="subsection">4.3.5 Privatization safety</h4> |
| |
| <p>Privatization safety is ensured by libitm using a quiescence-based approach. |
| Basically, a privatizing transaction waits until all concurrent active |
| transactions will either have finished (are not active anymore) or operate on |
| a sufficiently recent snapshot to not access the privatized data anymore. This |
| happens after the privatizing transaction has stopped being an active |
| transaction, so waiting for quiescence does not contribute to deadlocks. |
| </p> |
| <p>In method groups that need to ensure publication safety explicitly, active |
| transactions maintain a flag or timestamp in the public/shared part of the |
| transaction descriptor. Before blocking, privatizers need to let the other |
| transactions know that they should wake up the privatizer. |
| </p> |
| <p><strong>TODO</strong> Ho to implement the waiters? Should those flags be |
| per-transaction or at a central place? We want to avoid one wake/wait call |
| per active transactions, so we might want to use either a tree or combining |
| to reduce the syscall overhead, or rather spin for a long amount of time |
| instead of doing blocking. Also, it would be good if only the last transaction |
| that the privatizer waits for would do the wake-up. |
| </p> |
| <a name="Progress-guarantees"></a> |
| <h4 class="subsection">4.3.6 Progress guarantees</h4> |
| <a name="progress_002dguarantees"></a> |
| <p>Transactions that do not make progress when using the current TM method will |
| eventually try to execute in serial mode. Thus, the serial lock’s progress |
| guarantees determine the progress guarantees of the whole TM. Obviously, we at |
| least need deadlock-freedom for the serial lock, but it would also be good to |
| provide starvation-freedom (informally, all threads will finish executing a |
| transaction eventually iff they get enough cycles). |
| </p> |
| <p>However, the scheduling of transactions (e.g., thread scheduling by the OS) |
| also affects the handling of progress guarantees by the TM. First, the TM |
| can only guarantee deadlock-freedom if threads do not get stopped. Likewise, |
| low-priority threads can starve if they do not get scheduled when other |
| high-priority threads get those cycles instead. |
| </p> |
| <p>If all threads get scheduled eventually, correct lock implementations will |
| provide deadlock-freedom, but might not provide starvation-freedom. We can |
| either enforce the latter in the TM’s lock implementation, or assume that |
| the scheduling is sufficiently random to yield a probabilistic guarantee that |
| no thread will starve (because eventually, a transaction will encounter a |
| scheduling that will allow it to run). This can indeed work well in practice |
| but is not necessarily guaranteed to work (e.g., simple spin locks can be |
| pretty efficient). |
| </p> |
| <p>Because enforcing stronger progress guarantees in the TM has a higher runtime |
| overhead, we focus on deadlock-freedom right now and assume that the threads |
| will get scheduled eventually by the OS (but don’t consider threads with |
| different priorities). We should support starvation-freedom for serial |
| transactions in the future. Everything beyond that is highly related to proper |
| contention management across all of the TM (including with TM method to |
| choose), and is future work. |
| </p> |
| <p><strong>TODO</strong> Handling thread priorities: We want to avoid priority inversion |
| but it’s unclear how often that actually matters in practice. Workloads that |
| have threads with different priorities will likely also require lower latency |
| or higher throughput for high-priority threads. Therefore, it probably makes |
| not that much sense (except for eventual progress guarantees) to use |
| priority inheritance until the TM has priority-aware contention management. |
| </p> |
| |
| |
| <hr> |
| <div class="header"> |
| <p> |
| Next: <a href="GNU-Free-Documentation-License.html#GNU-Free-Documentation-License" accesskey="n" rel="next">GNU Free Documentation License</a>, Previous: <a href="The-libitm-ABI.html#The-libitm-ABI" accesskey="p" rel="prev">The libitm ABI</a>, Up: <a href="index.html#Top" accesskey="u" rel="up">Top</a> [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Library-Index.html#Library-Index" title="Index" rel="index">Index</a>]</p> |
| </div> |
| |
| |
| |
| </body> |
| </html> |