• No results found

Concurrent programming and transactional memory

Until recently, the mainstream programming model was sequential. This programming practice was widespread because the previous generation of processor architecture, the uni-processor, could only perform instructions one after the other. For this generation of processors the increasing demand in processing power was satisfied by building faster and more powerful uni-processors. However, this trend has recently been broken, since hardware manufacturers could not practically continue increasing processing power of a single processor. Instead they offered a new generation of processors that incorporate multiple cores with average computing power integrated into a single chip [145, 179]. This change in processor architecture has a direct impact on the software development, because exploiting the computing power of the new processing architectures now absolutely necessitates parallelization. Hence, mainstream programming practice should now change its course towards concurrent programming [179,180,90,113,48].

Unfortunately, concurrent programming is more challenging than its sequential coun- terpart because it introduces additional difficulties for the programmer [180,113, 48, 90]. A concurrent program design requires coordinating concurrently executing sequential tasks. In a way, a programmer needs to deal with multiple sequentially executing entities at the same time. Moreover, those sequential execution entities1 are meant to communicate with each other to achieve a final common objective. A common challenge in such systems is to mask the non-determinism of the program state observed due to concurrency. In sequen- tial programs the state of the program is well-defined by the set of instructions previously performed by the single execution entity. In a concurrent program, this is not anymore true

since between the execution of one instruction of a given execution entity to the next, other execution entities could change the program state. Despite this non-determinism of the program state, a concurrent program should still deliver a deterministic and correct result. This is one of the major reasons why parallel programming has been considered to be a difficult task.

Since the architectural trends force mainstream programmers to become concurrent programmers, recently researchers started looking for solutions that simplify the job of coding concurrent programs. As done for solving most of complex engineering problems, the current quest is to find new abstraction(s) that hide(s) most of the complexity of concurrent programming such that the programmer could focus on the task rather than struggling with difficulties merely due to the concurrency in the design.

The current widespread concurrent programming approach is lock-based programming [9,113,90]. This approach is appealing for two reasons:

ˆ It applies to the shared memory programming model. This model is considered to be convenient to program with because the concurrent execution entities share the same memory space and hence there is no need to explicitly transfer data from one execution entity to the other. Instead, each entity can obtain/modify the data it is interested in, simply by accessing it at the corresponding memory address, as it would do in a sequential program.

ˆ The programmer can mark the code sections, named critical sections, where s/he knows there will be accesses to shared data. By enclosing code into critical sections the programmer actually requests exclusive access to the shared data as long as the execution stays in the critical section. The exclusive access in critical sections is provided by acquisition and release of locks at the beginning and at the end of critical sections respectively. This programming discipline allows the programmer to code the same way as a sequential program all through the execution entity code.

Although having appealing properties, the main problem with lock-based programming is that the programmer needs to specify locks manually to enter and to quit critical sections. This is an error-prone process where the mapping of the shared data to the lock that protects it should be done manually by the programmer. Apart from that, the concurrency of operations in execution entities makes the use of locks even more difficult and results in classical problems such as deadlocks, livelocks, priority inversion etc. (see Section 2.2for more details). Since handling these problems is not trivial, lock-based programming is not well suited as a concurrent programming paradigm [180,9,113,90].

In the last decade one approach that attracted a lot of attention, especially in multi- threaded application domain, is to write concurrent programs using the runtime support called Transactional Memory (TM). TM can be considered as a system running under the multi-threaded application to provide the consistency of the accesses to shared data without explicit intervention of the programmer (such as managing the locks). TM is capable of doing this by introducing the transaction abstraction to the programmer (this type of transaction is generally called memory transaction in the literature to distinguish it from database transaction [55]). With this abstraction the programmer can group a set of operations such that they appear to be executed in a single step to other threads. Another convenient property of the abstraction is isolation where the transaction ensures that it runs as if it were the only thread running in the application. These two properties allow the programmer to ignore the operations concurrent to the transaction, as if they are not being executed. This way, writing a multi-threaded program using the transaction abstraction gets closer to writing a single-threaded sequential program.

Writing programs using TM, more specifically using the transaction abstraction provided by TM, necessitates a different way of coding and design compared to lock-based program- ming and, hence, the corresponding programming paradigm is generally called TM-based programming. At first sight, TM-based programming may be considered the same as programming with databases, since both are based on using the transaction abstraction. However, programming that makes use of databases isolates the data under the control of a database from the rest of the application data. Furthermore, accessing data in a database is performed through a set of functions. TM-based programming opts for simplicity of programming and hence eliminates both the isolation of data1 and the data access through a function interface [55, 90]. The programmer using TM only needs to spot the code sections that should execute in a transaction and to mark them in the code. Note that, when compared to lock-based programming, marking such code sections is even simpler in TM-based programming since the programmer does not need to manage locks.

1

The elimination of isolation simplifies programming for the application programmer since s/he does not need to think whether data is stored in a database or not, instead it shifts the burden to the TM designer. The data isolated in a database can only be accessed transactionally through the database. A notable difference of TM-based programming (introduced through the elimination of isolation) is that data can be accessed both in a transactional manner and directly, and if care is not taken there can be transactional and direct concurrent accesses to the same data, introducing inconsistencies in application execution [55,26,90].