#pragma omp atomic

Purpose

The omp atomic directive allows access of a specific memory location atomically. It ensures that race conditions are avoided through direct control of concurrent threads that might read or write to or from the particular memory location. With the omp atomic directive, you can write more efficient concurrent algorithms with fewer locks.

Syntax

Read syntax diagramSkip visual syntax diagram
                          .-update--.   
>>-#--pragma--omp atomic--+---------+--------------------------->
                          +-read----+   
                          +-write---+   
                          '-capture-'   

>--+-expression_statement-+------------------------------------><
   '-structured_block-----'   

where expression_statement is an expression statement of scalar type, and structured_block is a structured block of two expression statements.

Clauses

update
Updates the value of a variable atomically. Guarantees that only one thread at a time updates the shared variable, avoiding errors from simultaneous writes to the same variable. An omp atomic directive without a clause is equivalent to an omp atomic update.
Note: Atomic updates cannot write arbitrary data to the memory location, but depend on the previous data at the memory location.
read
Reads the value of a variable atomically. The value of a shared variable can be read safely, avoiding the danger of reading an intermediate value of the variable when it is accessed simultaneously by a concurrent thread.
write
Writes the value of a variable atomically. The value of a shared variable can be written exclusively to avoid errors from simultaneous writes.
capture
Updates the value of a variable while capturing the original or final value of the variable atomically.
The expression_statement or structured_block takes one of the following forms, depending on the atomic directive clause:
Directive clause expression_statement structured_block
update

(equivalent to no clause)

x++;

x--;

++x;

--x;

x binop = expr;

x = x binop expr;

x = expr binop x;

 
read

v = x;

 
write

x = expr;

 
capture

v = x++;

v = x--;

v = ++x;

v = --x;

v = x binop = expr;

v = x = x binop expr;

v = x = expr binop x;

{v = x; x binop = expr;}

{v = x; xOP;}

{v = x; OPx;}

{x binop = expr; v = x;}

{xOP; v = x;}

{OPx; v = x;}

{v = x; x = x binop expr;}

{x = x binop expr; v = x;}

{v = x; x = expr binop x;}

{x = expr binop x; v = x;}

{v = x; x = expr;}1

Note:
  1. This expression is to support atomic swap operations.
where:
x, v
are both lvalue expressions with scalar type.
expr
is an expression of scalar type that does not reference x.
binop
is one of the following binary operators:
+  *  -  /  &  ^  |  <<  >>
OP
is one of ++ or --.
Note: binop, binop=, and OP are not overloaded operators.

Usage

Objects that can be updated in parallel and that might be subject to race conditions should be protected with the omp atomic directive.

All atomic accesses to the storage locations designated by x throughout the program should have a compatible type.

Within an atomic region, multiple syntactic occurrences of x must designate the same storage location.

All accesses to a certain storage location throughout a concurrent program must be atomic. A non-atomic access to a memory location might break the expected atomic behavior of all atomic accesses to that storage location.

Neither v nor expr can access the storage location that is designated by x.

Neither x nor expr can access the storage location that is designated by v.

All accesses to the storage location designated by x are atomic. Evaluations of the expression expr, v, x are not atomic.

For atomic capture access, the operation of writing the captured value to the storage location represented by v is not atomic.

Examples

Example 1: Atomic update

extern float x[], *p = x, y;

/* Protect against race conditions among multiple updates. */
#pragma omp atomic
x[index[i]] += y;

/* Protect against race conditions with updates through x. */
#pragma omp atomic
p[i] -= 1.0f;

Example 2: Atomic read, write, and update

extern int x[10];
extern int f(int);
int temp[10], i;

for(i = 0; i < 10; i++)
{
  #pragma omp atomic read
  temp[i] = x[f(i)];
  
  #pragma omp atomic write
  x[i] = temp[i]*2;
  
  #pragma omp atomic update
  x[i] *= 2;
}

Example 3: Atomic capture

extern int x[10];
extern int f(int);
int temp[10], i;

for(i = 0; i < 10; i++)
{
  #pragma omp atomic capture
  temp[i] = x[f(i)]++;
  
  #pragma omp atomic capture
  {
    temp[i] = x[f(i)]; //the two occurences of x[f(i)] must evaluate to the
    x[f(i)] -= 3; //same memory location, otherwise behavior is undefined.
  }
}