• No results found

6.2 Automatic transactification with Java

6.2.1 Transactification overview

6.4.2.5 Exception handling

As described in Section 5.6.1 there is no language extension feature required to support exception handling of transaction blocks. Conversely, atomic boxes and the corre- sponding enhanced exception handling constructs introduced in Section 5.6.2 require an implementation. However, since the atomic boxes represent an original extension, all the

details about them, including the implementation (see Section 7.6) is presented in Chap- ter 7.

6.5

Summary

In this chapter, we presented our automatic transactification process which is composed of two steps: code restructuring (also called source-to-source transformation) and bytecode instrumentation. We have explained the role of each step, and focused on our tool TMJava, that performs the code restructuring and that has been developed as part of the thesis work (the other step is performed by a different tool, Deuce, developed at Tel Aviv University). In the transactification process the insertion of the transactional code is performed by the bytecode instrumentation step, however, this tool can produce transactional code only for properly annotated class methods. The role of the TMJava tool is to restructure the code such that all code that needs to execute in a transaction is mapped into a method(s) with the necessary annotation. We also describe how TMJava generates code to support irrevocability, control flow (regular, transactional or exceptional control flow), alternative execution paths, and transactional nesting.

As it performs code restructuring, TMJava is the tool that enables the specification that is described in Chapter5. Based on an extensible Java compiler, JastAdd, TMJava is a flexible language extension specification implementation. Modifications or enhancements on the specification of Chapter5can be easily adapted to TMJava. This shows the power of our two step automatic transactification process: allowing flexibility on the language specification side while providing a simple methodology (bytecode instrumentation based on method annotations) for generating transactional code.

Atomic Boxes: Coordinated

Exception Handling with TM

The transaction abstraction implemented by TM is mostly considered as a means to provide data synchronization. However, the transaction concept is more powerful than just being a data synchronization aid and has the potential to be useful on other aspects of concurrent programming. This chapter illustrates how to make use of the transaction abstraction provided by an underlying TM to improve failure recovery of multi-threaded applications. Since detection of failures and their handling is performed through the exception handling mechanism in Java, our approach is to introduce a transaction abstraction to enhance the Java exception handling.

We introduce the transaction abstraction as a language construct as part of our language extension described in Chapter 5 and we present it as follows. First, we describe the problem for which the language construct could be useful in Section7.1. We then give an overview of the solution proposed by the language construct in Section 7.2. Section 7.3 presents closely related work. Section7.4 presents the example that is used subsequently to illustrate our language construct. Section 7.5 describes the syntax and semantics of the language constructs for coordinated exception handling. Section 7.6 presents the implementation details of coordinated exception handling in terms of code transformations (as part of TMJava). Section 7.7 compares the performance we obtained against the competing alternative approach and Section 7.8summarizes the chapter.

7.1

Problem

Exceptions and exception handling mechanisms are effective means in redirecting the control flow of an error-prone sequential program before it executes on an inconsistent state of the system. This fact has led to extensive studies on exception handling mechanisms and their being tailored to work well with sequential programs. At the same time, a recent survey on 32 sequential applications presents the general picture on the exception handling usage by the programmers and reports that even though more than 4% of the total source code is dedicated to it, exception handling is neglected in most of the cases: either terminating the program or ignoring the exception [29]. This result shows that sequential programs are generally developed by using exceptions as a means to terminate programs in a convenient way and inconsistencies resulting from exceptional situations are not really handled.

In concurrent programs, however, an exception raised by one thread interrupts the execution of the thread’s task, but this situation does not prevent other threads from continuing to execute their own tasks. This implies that an exception raised on a thread can bring some shared data to inconsistent state (at least temporarily until the exception is handled) and since other threads are not blocked upon the exception (they are unaware of the raised exception) they can access this inconsistent shared state. However, such an exception (one whose cause affect multiple threads due to shared state) should ideally be detected by all the threads that operate on the same shared state. Two solutions to the problem can be considered: (i) the program should be brought back to a consistent state by handling the exception, or (ii) all the affected threads (or even the whole application) should be terminated to avoid execution on inconsistent shared states. Since there are no widespread mechanisms that allow any of the solutions, it is up to programmers to devise a solution for such cases. In other words, compared to sequential programs where treating exceptions is barely considered, for concurrent programs handling exceptions should be part of the main application design and development in order not to jeopardize the application correctness.

Consider the code in Figure 7.1(inspired by a similar example in [176]) that illustrates how easily the above-mentioned inconsistency problem can appear in concurrent programs written using regular programming language constructs, i.e., without transactions. The figure presents a naive implementation of a classifier program where multiple threads con- currently evaluate nodes from the unclassifiedNodes list, process them, and move them to the target class using the assignToClass method. Note that we assume both the unclassifiedNodes list and the target classes class[N] are shared by all threads.

1 Class AlgorithmA {

2 int N; // number of classes

3 List unclassifiedNodes;

4 Set class[N];

5 ...

6 public void assignToClass(int srcPos, int targetClass) {

7 synchronized(this) {

8 Node selectedNode = unclassifiedNodes.remove(srcPos);

9 selectedNode.transform();

10 class[targetClass].add(selectedNode);

11 }

12 }

13 }

Figure 7.1: A concurrent code that may end up in an inconsistent state if an ex- ception is raised while the selected node’s representation is being transformed (in the selectedNode.transform() function) as required by the target class object.

If an exception is raised on line9and is not handled, the system reaches an inconsistent shared state: the selectedNode gets lost as it is neither in the unclassifiedNodes nor in its target class. For correct execution of the program, the exception should be handled and this should be performed before any of the other threads, unaware of the raised exception, access either the unclassifiedNodes list or the target class which are inconsistent. Hence, the handling of the exception should take the existence of concurrent threads into account. This example, albeit naive, clearly shows that exception handling becomes a first class design consideration in development of correct concurrent programs. And, needless to say, with the mainstream computer hardware becoming multi-core, concurrent programming is about to become mainstream too. This fact highlights the need for solutions that will simplify concurrent programming under exceptional situations.