Go to main content
Oracle® Developer Studio 12.6: OpenMP API User's Guide

Exit Print View

Updated: June 2017
 
 

4.7 OpenMP Programming Considerations

Tasking introduces a layer of complexity to an OpenMP program. This section discusses some task-related programming issues to consider.

4.7.1 Threadprivate and Thread-Specific Information

When a thread encounters a task scheduling point, the implementation might suspend the current task and schedule the thread to work on another task. This behavior implies that threadprivate variables or other thread-specific information such as the thread number in a task might change across a task scheduling point.

If the suspended task is tied, then the thread that resumes executing the task will be the same thread that suspended it. Therefore, the thread number will remain the same after the task is resumed. However, the value of a threadprivate variable might change because the thread might have been scheduled to work on another task that modified the threadprivate variable before resuming the suspended task.

If the suspended task is untied, then the thread that resumes executing the task might be different from the thread that suspended it. Therefore, both the thread number and the value of threadprivate variables before and after the task scheduling point might be different.

4.7.2 OpenMP Locks

Since OpenMP 3.0, locks are owned by tasks, not by threads. Once a lock is acquired by a task, the task owns it, and the same task must release it before the task is completed. However, the critical construct remains a thread-based mutual exclusion mechanism.

Because locks are owned by tasks, take extra care when using locks. The following example conforms to the OpenMP 2.5 specification because the thread that releases the lock lck in the parallel region is the same thread that acquired the lock in the sequential part of the program. The master thread of the parallel region and the initial thread are the same. However, the example does not conform to later specifications because the task region that releases the lock lck is different from the task region that acquired the lock.

Example 9  Using Locks Prior to OpenMP 3.0
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main()
{
  int x;
  omp_lock_t lck;

  omp_init_lock (&lck);
  omp_set_lock (&lck);
  x = 0;

  #pragma omp parallel shared (x)
  {
    #pragma omp master
    {
      x = x + 1;
      omp_unset_lock (&lck);
    }
  }
  omp_destroy_lock (&lck);
}

4.7.3 References to Stack Data

A task may reference data on the stack of the routine where the task construct appears (the host routine). Because the execution of a task may be deferred until the next implicit or explicit barrier, a task could execute after the stack of the host routine has already been popped and the stack data overwritten, thereby destroying the stack data referenced by the task.

Be sure to insert the needed synchronizations so that variables are still on the stack when the task references them, as illustrated in the two examples in this section.

In Example 10, i is specified to be shared in the task construct, and the task accesses the copy of i that is allocated on the stack of the work() routine.

Task execution may be deferred, so the task may be executed at the implicit barrier at the end of the parallel region in main() after the work() routine has already returned. At that point, when the task references i, it accesses some undetermined value that happens to be on the stack at that time.

For correct results, make sure that work() does not return before the task has completed. This can be accomplished by inserting a taskwait directive after the task construct, as shown in Example 11. Alternatively, i can be specified to be firstprivate in the task construct, instead of shared.

Example 10  Stack Data: Incorrect Reference
#include <stdio.h>
#include <omp.h>

void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }
Example 11  Stack Data: Corrected Reference
#include <stdio.h>
#include <omp.h>

void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }

   /* Use TASKWAIT for synchronization. */
   #pragma omp taskwait
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }

In the following example, j in the task construct refers to the j in the sections construct. Therefore, the task accesses the firstprivate copy of j in the sections construct, which in Oracle Developer Studio is a local variable on the stack of the outlined routine for the sections construct.

Task execution may be deferred so the task may be executed at the implicit barrier at the end of the sections region after the outlined routine for the sections construct has exited. Therefore, when the task references j, it accesses some undetermined value on the stack.

For correct results, make sure that the task is executed before the sections region reaches its implicit barrier by inserting a taskwait directive after the task construct as shown in Example 13. Alternatively, j can be specified to be firstprivate in the task construct, instead of shared.

Example 12  Sections Data: Incorrect Reference
#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }
          }
       } /* Implicit barrier for sections */
    } /* Implicit barrier for parallel */

    printf("After parallel, j = %d\n",j);
 }
Example 13  Sections Data: Corrected Reference
#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }

             /* Use TASKWAIT for synchronization. */
             #pragma omp taskwait
          }
       } /* Implicit barrier for sections */
    }/* Implicit barrier for parallel */

    printf("After parallel, j = %d\n",j);
 }