• No se han encontrado resultados

FORMULARIO DE POSTULACIÓN ANEXO 2. COMPROMISO DEL PARTICIPANTE

Privatization occurs when a thread makes a(n) variable/object inaccessible to other threads by using a synchronization mechanism [113, 130]. Hence, privatization is a phase in the multi-threaded program (conceptually the reverse of publication) where a piece of data switches from shared usage to private usage using a synchronization mechanism.

The privatization pattern, which is symmetric to the publication pattern, is illustrated on the right side of Figure 2.1. In this pattern, SG1 is a group of statements where the privatized data is accessed directly, the privatizing action is the transaction that privatizes the data (named generally privatizing transaction), and SG2 is a group of statements that access the privatized data assuming that data is still shared.

Privatization safety is the ordering guarantee enforcing that all the non-transactional access in SG1 occurs after any of accesses in SG2 if the transaction enclosing SG2 should logically be ordered before the privatizing transaction (hence Figure2.1depicts the desired ordering). Privatization safety is hence violated when some actions (or their effects) in SG2 are delayed and actually occur after the privatizing transaction, causing data races between the transactional accesses in SG2 and non-transactional accesses in SG1. The situations

shown in the example for optimization) and, hence, the code of the programmer is semantically correct and a programming language providing such semantics for transactions should behave according to this semantics.

where privatization safety is violated can be classified in two groups: delayed cleanup and delayed conflict detection.

Delayed cleanup problem [128,169,116,90] occurs when private accesses of SG1 in Figure2.1are interleaved with a commit or an abort operation of SG2 where modifications to be performed by the commit or abort operation are delayed to take effect and are ordered after the privatizing transaction. Figure2.3illustrates the inconsistent read and lost update problems that can occur due to delayed cleanup both in direct and deferred update STMs1 (a detailed explanation of the example is deferred to the end of the section). As it can be noticed, the violation of privatization due to delayed cleanup causes incorrect behavior of the non-transactional accesses only.

Delayed conflict detection problem occurs when the transaction enclosing SG2 con- tinues to execute concurrent to the statements of SG1 until it becomes aware that it is invalidated (thus made doomed to abort) by a committed privatizing transaction2 . The concurrent accesses in SG1 and in the invalidated transaction can result in infinite loops, null reference exceptions, divide-by-zero errors since the invalidated transaction can per- form reads and deliver inconsistent values. Figure2.4depicts an inconsistent read example (line 33) causing the program to crash due to a null reference. The figure also illustrates inconsistent reads that can be observed by the non-transactional accesses for direct update STMs.

Details on the example used in Figures 2.3 and 2.4: In our example, we see two threads, Thread 1 and Thread 2, executing according to specific schedules and acting on a shared list L. The elements of the list have two fields: val and flag. Thread 1 privatizes the shared list L using transaction T1. The rest of Thread 1’s actions is performed under the assumption that the shared list is only accessed by Thread 1. In this part of its code Thread 1 traverses the list, updates the val fields of the elements, counts the number of elements where the flag field is set and stops the traversal (deallocating the rest of the list with the free list function) if the updated value of the visited element is higher than a threshold y. Thread 2, inside its transaction T2, traverses the list up to an element of the list which represents a val field above the threshold x and updates all the fields of the elements it traverses. Due to the initial values of the list L and the thresholds x and y both

1

Direct update STMs make their updates on shared data items visible to other threads right away (without waiting the commit), whereas deferred update STMs log the updates during transaction execution and make their updates effective during transaction commit. See details on direct and deferred update STMs in SectionA.3.2.

2

This class of privatization violation is also known as doomed transaction problem since it is always caused by the transaction enclosing SG2 which is invalidated by the privatizing transaction. However, we prefer to distinguish this class of violation as delayed conflict detection since doomed transactions also cause delayed cleanup problems for direct-update systems as seen in Figure2.3.

Initially the shared list L has two elements. The list, variables x,y and thread-local variable counter have the following content: List elements: {val=8, flag=false, next=...}, {val=13, flag=false, next=NULL}, x=10; y=15; counter=0;

Direct update STM Deferred update STM

1 // Thread1 // Thread2 2 // Transaction T2 3 atomic{ 4 if(L.head != NULL){ 5 // Transaction T1 (privatizer) 6 atomic{ 7 privL.head = L.head; 8 n = L.head; 9 while( n.val < x){ 10 n.flag = true; 11 n.val = n.val+2; 12 if (n.next == NULL) 13 break; 14 else 15 n = n.next; 16 } // end of while 17 } // end of if 18 L.head = NULL; 19 }

20 // abort preempted before

21 // rollback 22 // private actions 23 n = privL.head; 24 while(n != NULL){ 25 n.val = 2*n.val; 26 if ( n.flag == true ) { 27 counter++; 28 } 29 if( n.val > y ) { 30 free_list(n.next); 31 n.next = NULL; 32 } 33 n = n.next; 34 } 35 } // abort terminated 1 // Thread1 // Thread2 2 // Transaction T2 3 atomic{ 4 if(L.head != NULL){ 5 // Transaction T1 (privatizer) 6 atomic{ 7 privL.head = L.head; 8 n = L.head; 9 while( n.val < x){ 10 n.flag = true; 11 n.val = n.val+2; 12 if (n.next == NULL) 13 break; 14 else 15 n = n.next; 16 } // end of while 17 } // end of if

18 // commit preempted before

19 // write back 20 L.head = NULL; 21 } 22 // private actions 23 n = privL.head; 24 while(n != NULL){ 25 n.val = 2*n.val; 26 if ( n.flag == true ) { 27 counter++; 28 } 29 if( n.val > y ) { 30 free_list(n.next); 31 n.next = NULL; 32 } 33 n = n.next; 34 } 35 } // commit terminated

Expected outcome: T2 is serialized after T1: First list element: {val=16, flag=false, next=NULL} Second list element: deleted

x=10; y=15; counter=0; Observed outcome:

First list element: {val=8, flag=false, next=NULL} Second list element: deleted

x=10; y=15; counter=1; Problematic accesses:

(line 25) inconsistent read: tentative value of n.val set by Thread 2 (i.e., 10) is read.

(line 26) inconsistent read: tentative value of n.flag set by Thread 2 (i.e., true) is read. Reason for inconsistent counter value.

(line 35) lost update: abort overrides n.val written on line 25 (i.e., n.val is restored as 8).

Expected outcome: T1 is serialized after T2: First list element: {val=20, flag=true, next=NULL} Second list element: deleted

x=10; y=15; counter=1; Observed outcome:

First list element: {val=10, flag=true, next=NULL} Second list element: deleted

x=10; y=15; counter=0; Problematic accesses:

(line 25) inconsistent read: uncommitted value of n.val (i.e., 8) is read.

(line 26) inconsistent read: uncommitted value of n.flag (i.e., false) is read. Reason for inconsistent counter value.

(line 35) lost update: commit overrides n.val written on line 25 (i.e., n.val becomes 10).

Initially the shared list L has two elements. The list, variables x,y and thread-local variable counter have the following content: List elements: {val=8, flag=false, next=...}, {val=13, flag=false, next=NULL}, x=10; y=15; counter=0;

Schedule Outcomes

1 // Thread1 // Thread2

2 // Transaction T2

3 atomic{

4 // Privatizer transaction T1 if(L.head != NULL){ 5 atomic{ 6 privL.head = L.head; 7 n = L.head; 8 L.head = NULL; 9 } 10 while( n.val < x){ 11 n.flag = true; 12 n.val = n.val+2; 13 if(n.next ==NULL) 14 break; 15 else 16 // private actions 17 n = privL.head; 18 while(n != NULL){ 19 n.val = 2*n.val; 20 if ( n.flag == true ) { 21 counter ++; 22 } 23 if( n.val > y ) { 24 free_list(n.next); 25 n.next = NULL; 26 } 27 n = n.next; 28 } 29 n = n.next; 30 }// end of while 31 // Beginning of 32 // second iteration 33 while( n.val < x){ 34 ...

Expected outcome: T2 is serialized after T1: Since L.head is NULL, T2 does nothing.

First list element: {val=16, flag=false, next=NULL} Second list element: deleted

x=10; y=15; counter=0; Observed outcome:

Program crashes at line 33 (access to NULL reference) Problematic accesses for direct update STMs: (line 19) inconsistent read: tentative value of n.val set by Thread 2 (i.e., 10) is read.

(line 20) inconsistent read: tentative value of n.flag set by Thread 2 (i.e., true) is read. Reason for inconsistent counter value.

Problematic access for direct&deferred update STMs: (line 29) inconsistent read: T2 reads non-transactionally modified (on line 25) next pointer (which is NULL).

Figure 2.4: Illustration of the data races caused by delayed conflict detection. Note that the figure illustrates a schedule where Thread 1’s code is interleaved with the first iteration of Thread 2’s while loop (Thread 2’s loop is unrolled). Beginning of the second iteration causes a null reference on line 33.

threads can only update the first element of L. More specifically, if T1 is serialized after T2, the private actions of Thread 1 observe L such that only its first element is modified with the val field set to 10 and the flag field set to true. If, however, T1 is serialized before T2, T2 should see L empty and should commit without doing anything.

Documento similar