Our atomic boxes implementation is currently at a preliminary stage but covers the fun- damentals of the atomic box semantics explained in Section 7.5. Our implementation is designed as part of the TMJava front-end tool (see Chapter6). As such, the implementa- tion consists of transforming the source code into plain Java code where the transaction and recover blocks are appropriately wrapped and augmented with code allowing the re- quired semantics.
A transaction block is mapped to a Deuce function annotated with an @Atomic as is done for transaction blocks without the atomic box semantics (explained in Sec- tion 6.4.2.1). The recover block is also mapped to a function but that is explicitly marked such that it does not execute in transactional context (the reason for an explicit indication is that the function is called from inside a transactional context for which Deuce automatically instruments a transactional version and performs a call to the transactional version instead of non-instrumented version, which we would like to avoid in this case). The body of a transaction block is placed into the corresponding Deuce function by enclosing it in a try block followed by a single catch block catching all Throwables. If an unhandled exception is propagated out of the body of a transaction block, this catch
block catches the exception and sets the status of the atomic box to failure (also records the raised exception inside a CancelException for all the members of the atomic box to see the raised exception).
To describe an atomic box, we define an AtomicBox class that contains, a field indicat- ing the status of the atomic box (failure or normal execution mode), a CancelException member storing the exception that caused the failure of the atomic box1, a counter indi- cating the number of transaction blocks that are in the active atomic box (to be used by barriers synchronizing threads; one before beginning recovery and one right after the recovery) and a second counter indicating the number of transaction blocks that have started committing but has not finished it yet. Since atomic boxes are defined statically in code using the name parameter of the transaction block, they are allocated and con- structed at the beginning of the program and they are used as shared objects throughout the program (except if the transaction blockis not named; in this case the atomic box is local to a thread). As with the function that corresponds to the recover block of the transaction-recover statement, the AtomicBox objects are accessed directly, and not through the TM, i.e., AtomicBox object accesses are excluded from transactification. This is required since AtomicBox objects carry information out of aborted transactions.
The Deuce function that corresponds to a transaction block contains some more code before and after the try-catch construct that encloses the contents of the transaction block. At the beginning of the function and before returning from the function (these points corresponds to start and commit of a transaction), this additional code checks whether the atomic box to which the current transaction block belongs is in failure mode. If this code finds out that the atomic box is in failure mode, it aborts the transaction for a restart. If it is the code at the beginning the Deuce function that observes the failure of the atomic box, it calls the function that corresponds to the recover block by passing the CancelException member of the atomic box to the function. The return value of this function is used to retry the transaction block or to quit the function (thus to apply transaction cancel semantics). If the function throws an exception the exception is propagated out of the Deuce function as well.
The function that corresponds to the recover block executes the contents of the recover block, but passes through a preceding and succeeding barrier, to ensure the syn- chronization of threads as explained in the steps 4 and 6 of the list of steps describing the coordinated behavior upon failure of an atomic box (see Section 7.5.2). Between the two barriers, the function checks its CancelException parameter’s handlingContext field to
1
a single exception storage is enough since only one exception is handled even when there are concurrently raised exceptions (see Section7.5.5)
see if it has to execute the recover block contents. If the handlingContext field has the value local it skips all statements in the recover block and enters the second barrier for the second synchronization of recovering threads (if the recover block corresponds to the initiator transaction block, the recover block contents are executed regardless of handlingContext field value). By default the function returns a value such that the caller Deuce function retries the transaction block. If the contents of the recover block requires, the return value is changed so that the caller Deuce function quits to provide transaction cancel semantics. Also, if the recover block raises an exception, a flag is set to indicate this, and the exception is raised only after the second barrier is crossed.
Since our implementation is at a preliminary stage, the design explained above is partially realized. Especially, the features required for the correct execution of atomic box form of a transaction block are not complete. For this reason, the examples in Figures 7.4-7.6 could not be tested with the current implementation. This, however, did not prevent us to perform the evaluations on the performance of the design (with respect to the performance of failboxes) that we present in the next section.