4.6. Synchronization Constructs

The following sections describe the synchronization constructs:

4.6.1. Request Execution by the Master Thread: MASTER and END MASTER Directives

The code enclosed within MASTER and END MASTER directives is executed by the master thread.

These directives have the following format:

!$OMP MASTER

block

!$OMP END MASTER

block 

Denotes a structured block of Fortran statements. You cannot branch into or out of the block.

The other threads in the team skip the enclosed section of code and continue execution. There is no implied barrier either on entry to or exit from the master section.

4.6.2. Request Execution by a Single Thread: CRITICAL and END CRITICAL Directives

The CRITICAL and END CRITICAL directives restrict access to the enclosed code to one thread at a time.

These directives have the following format:

!$OMP CRITICAL [(name)]

block

!$OMP END CRITICAL [(name)]

name 

Identifies the critical section.

If a name is specified on a CRITICAL directive, the same name must also be specified on the END CRITICAL directive. If no name appears on the CRITICAL directive, no name can appear on the END CRITICAL directive.

block 

Denotes a structured block of Fortran statements. You cannot branch into or out of the block.

A thread waits at the beginning of a critical section until no other thread in the team is executing a critical section with the same name. All unnamed CRITICAL directives map to the same name. Critical section names are global entities of the program. If a name conflicts with any other entity, the behavior of the program is undefined.

Example. The following code fragment includes several CRITICAL directives. The example illustrates a queuing model in which a task is dequeued and worked on. To guard against multiple threads dequeuing the same task, the dequeuing operation must be in a critical section. Because there are two independent queues in this example, each queue is protected by CRITICAL directives with different names, XAXIS and YAXIS, respectively.

!$OMP PARALLEL DEFAULT(PRIVATE) SHARED(X,Y)
!$OMP CRITICAL(XAXIS)
      CALL DEQUEUE(IX_NEXT, X)
!$OMP END CRITICAL(XAXIS)
      CALL WORK(IX_NEXT, X)
!$OMP CRITICAL(YAXIS)
      CALL DEQUEUE(IY_NEXT,Y)
!$OMP END CRITICAL(YAXIS)
      CALL WORK(IY_NEXT, Y)
!$OMP END PARALLEL

4.6.3. Synchronize All Threads in a Team: BARRIER Directive

The BARRIER directive synchronizes all the threads in a team. When it encounters a barrier, a thread waits until all other threads in that team have reached the same point.

This directive has the following format:

!$OMP BARRIER

4.6.4. Protect a Location from Multiple Updates: ATOMIC Directive

The ATOMIC directive ensures that a specific memory location is updated atomically, rather than exposing it to the possibility of multiple, simultaneous writing threads.

This directive has the following format:

!$OMP ATOMIC

This directive applies only to the immediately following statement, which must have one of the following forms:

x = x operator expr

x = expr operator x

x = intrinsic (x, expr)

x = intrinsic (expr, x)

In the preceding statements:

Only the load and store of x are atomic; the evaluation of expr is not atomic. To avoid race conditions, all updates of the location in parallel must be protected with the ATOMIC directive, except those that are known to be free of race conditions.

Example 1. The following code fragment uses the ATOMIC directive:

!$OMP ATOMIC
      X(INDEX(I)) = Y(INDEX(I)) + B

Example 2. The following code fragment avoids race conditions by protecting all simultaneous updates of the location, by multiple threads, with the ATOMIC directive:

!$OMP PARALLEL DO DEFAULT(PRIVATE) SHARED(X,Y,INDEX,N)
      DO I=1,N
        CALL WORK(XLOCAL, YLOCAL)
!$OMP ATOMIC
        X(INDEX(I)) = X(INDEX(I)) + XLOCAL
        Y(I) = Y(I) + YLOCAL
      ENDDO

Note that the ATOMIC directive applies only to the Fortran 90 statement that immediately follows it. As a result, Y is not updated atomically in the preceding code.

4.6.5. Read and Write Variables to Memory: FLUSH Directive

The FLUSH directive identifies synchronization points at which thread-visible variables are written back to memory. This directive must appear at the precise point in the code at which the synchronization is required.

Thread-visible variables include the following data items:

This directive has the following format:

!$OMP FLUSH [(var[, var] ...)]

var 

Variables to be flushed.

An implicit FLUSH directive is assumed for the following directives:

The directive is not implied if a NOWAIT clause is present.

Example. The following example uses the FLUSH directive for point-to-point synchronization between pairs of threads:

!$OMP PARALLEL DEFAULT(PRIVATE) SHARED(ISYNC)
      IAM = OMP_GET_THREAD_NUM()
      ISYNC(IAM) = 0
!$OMP BARRIER
      CALL WORK()
!
!I AM DONE WITH MY WORK, SYNCHRONIZE WITH MY NEIGHBOR
!
      ISYNC(IAM) = 1
!$OMP FLUSH(ISYNC)
!
!WAIT TILL NEIGHBOR IS DONE
!
      DO WHILE (ISYNC(NEIGH) .EQ. 0)
!$OMP FLUSH(ISYNC)
      END DO
!$OMP END PARALLEL

4.6.6. Request Sequential Ordering: ORDERED and END ORDERED Directives

The code enclosed within ORDERED and END ORDERED directives is executed in the order in which it would be executed in a sequential execution of an enclosing parallel loop.

These directives have the following format:

!$OMP ORDERED

block

!$OMP END ORDERED

block 

Denotes a structured block of Fortran statements. You cannot branch into or out of the block.

An ORDERED directive can appear only in the dynamic extent of a DO or PARALLEL DO directive. This DO directive must have the ORDERED clause specified. For more information on the DO directive, see Section 4.4.1. For information on directive binding, see Section 4.8.

Only one thread is allowed in an ordered section at a time. Threads are allowed to enter in the order of the loop iterations. No thread can enter an ordered section until it is guaranteed that all previous iterations have completed or will never execute an ordered section. This sequentializes and orders code within ordered sections while allowing code outside the section to run in parallel. ORDERED sections that bind to different DO directives are independent of each other.

The following restrictions apply to the ORDERED directive:

Example. Ordered sections are useful for sequentially ordering the output from work that is done in parallel. Assuming that a reentrant I/O library exists, the following program prints out the indexes in sequential order:

!$OMP DO ORDERED SCHEDULE(DYNAMIC)
      DO I=LB,UB,ST
        CALL WORK(I)
      END DO

      SUBROUTINE WORK(K)
!$OMP ORDERED
      WRITE(*,*) K
!$OMP END ORDERED
      END