As mentioned above, the goal of a transactional memory system is to execute transactions in paral- lel while preserving transactions’ semantic properties. The basic framework for accomplishing this goal is to execute transactionsspeculativelyin parallel, detectconflictsthat would violate serializ- ability, andresolvethese conflicts by stalling or rolling back transactions to preserve serializability. As a result, transactional memory systems have three main tasks: conflict detection, conflict reso- lution, and version management (i.e., supporting the ability to roll back to a pre-speculative state). Below we discuss ideas and terminology associated with accomplishing these tasks. In the next section we will present several high-level transactional memory algorithms that systems can seek to implement.
2.3.1 Conflict Detection
Because transactions are executing in parallel, they may access the same data simultaneously. If at least one of the accesses is a write, a potential violation of isolation exists: a transaction’s update is potentially being observed by other threads before it commits. The role of conflict detection is to detect such potential violations (referred to asconflicts).
The basic way to detect conflicts is to monitor the addresses read and written by transactions, detecting cases where transactions simultaneously access the same address. We refer to this method of performing conflict detection as address-based conflict detection. The set of addresses that a transaction reads is called itsread set, and the set of addresses that a transaction writes is called its
action’s write set intersects with the read or write set of a simultaneously-executing transaction. A system may maintain the addresses in read and write sets at a precise byte granularity or at a coarser granularity (e.g., hardware transactional memory systems generally perform conflict detection at a memory block granularity, as discussed in Section 2.6).
Address-based conflict detection comes in two basic forms: eager and lazy [77]. In eager conflict detection, the system checks each memory access made by a transaction for conflicts with all other concurrently-executing transactions. In lazy conflict detection, by contrast, the system performs conflict detection for a given transaction at the time that that transaction seeks to commit. In either case, once a conflict is detected, the system must resolve the conflict in a way that preserves serializability. We discuss a variety of ways in which this task can be accomplished in the next subsection.
Prior work has noted that eager and lazy conflict detection have different performance charac- teristics [15]. By detecting conflicts as soon as possible, eager conflict detection can reduce wasted work. Conversely, lazy conflict detection may avoid the need to resolve some conflicts altogther. Consider an execution where one transaction reads address Aand a second transaction writes A, with the reader seeking to commit first. Eager conflict detection would detect the conflict and have to resolve it. Under lazy conflict detection, by contrast, both the reader and the writer can commit. We will examine the performance impact of these differences in Section 3.3.
To achieve the best performance characteristics of both eager and lazy conflict detection, recent work has proposed mixed eager/lazy policies [98, 112]. As discussed in Section 8.4, RETCONwill also realize the benefits of a mixed eager/lazy conflict detection policy (in addition to other benefits). We finally note that a different mechanism of detecting conflicts is to check whether any val- ues read by a transaction have been changed by another transaction [82, 109]. We refer to this mechanism for conflict detection asvalue-based conflict detection. For much of this dissertation we will strictly be concerned with address-based conflict detection. However, in Section 3.3 we will examine the benefits of incorporating value-based conflict detection. As discussed in Section 8.4, RETCONrealizes these benefits as well.
2.3.2 Conflict Resolution
Once a conflict has been detected, the system needs toresolvethe conflict in order to ensure that serializability is preserved. The most basic way to resolve a conflict is to roll back one of the transactions involved in the conflict (implemented as described below). This transaction can either be the transaction making the conflicting access or the transaction with whom the access conflicts. These basic policies, however, do not guarantee starvation avoidance.
A more sophisticated way to resolve conflicts is to use the age of the transactions involved [56, 89]. Inage-based conflict resolution, each transaction is assigned atimestampwhen it first begins. A transaction retains its timestamp until it commits (i.e., if it aborts and restarts, it retains the same timestamp). When a conflict occurs, these timestamps are used to resolve it as follows. If the requestee is older than the requester, the requester is stalled until the requestee releases isolation on the conflicting address (i.e., commits or aborts). Otherwise, the requestee aborts. In other words, a transaction wins conflicts with younger transactions and loses conflicts with older transactions. This policy provides a starvation avoidance guarantee: once a transaction becomes the oldest in the system, it is guaranteed not to be aborted by any other transaction [89].
Other conflict resolution policies have been explored in the literature. For example, Moore et al. [77] propose a policy in which the requestee stalls the requester unless it (the requestee) is already being stalled. We refer the reader to Larus and Rajwar [57] for other proposals. As prior work [15, 98] has shown that age-based conflict resolution generally has robust performance, we employ this policy throughout this dissertation.
We finally note that a different mechanism for resolving conflicts that is often employed by systems using lazy conflict detection is to employ acommit token. In systems employing a com- mit token, each transaction arbitrates for permission to commit by seeking to acquire the commit token. Once a transaction acquires the commit token, the system performs conflict detection for the transaction by checking whether any of the addresses in the transaction’s write set have been read by other, as-yet uncommitted transactions. If so, the conflict is resolved by rolling back the uncommitted transaction — as the other transaction has in effect already committed, there are no other options. To avoid starvation in such a system, a transaction that is continually aborted may obtain the commit token early and hold it until it commits [41].
2.3.3 Version Management
Version management is the task of enabling a transaction to roll back its state. This task involves undoing any updates that the transaction has made to restore all updated blocks to their pretransac- tional state.
Like conflict detection, version management can be eager or lazy [77]. In eager version manage- ment, transactional writes are performed in-place. Before a transaction writes a location for the first time, it logs the pretransactional value of the location. At transaction commit, written memory loca- tions already contain their correct values. To roll back, the transaction restores this pretransactional value.
In lazy version management, transactions perform writes into a private buffer. Transactional reads must check this buffer for forwarding before reading from memory. To commit, the transac- tion performs all its writes from the buffer into memory. Rollback can be accomplished simply by throwing away the buffer.
Again similar to conflict detection, eager and lazy version management have performance trade- offs [77]. In eager version management, commits can be fast but rollback involves restoring prespec- ulative state. Conversely, lazy version management enables fast aborts at the expense of commits. Additionally, lazy version management introduces an indirection into transactional reads that is not present in eager version management.