A task is likely to have references to data on the stack of the routine where the task construct appears. Since the execution of a task may be deferred until the next implicit or explicit barrier, it is possible that a given task will execute after the stack of the routine where it appears has already been popped and the stack data overwritten, thereby destroying the stack data listed as shared by the task.
It is the programmer's responsibility to insert the needed synchronizations to ensure that variables are still on the stack when the task references them. Here are two examples.
In the first example, 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 work().
Task execution may be deferred, so tasks are executed at the implicit barrier at the end of the parallel region in main() after the work() routine has already returned. So when a task references i, it accesses some undetermined value that happens to be on the stack at that time.
For correct results, the programmer needs to make sure that work() does not exit before the tasks have completed. This is done by inserting a taskwait directive after the task construct. Alternatively, i can be specified to be firstprivate in the task construct, instead of shared.
#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(); } } |
#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 our second example, j in the task construct is shared with the j in the sections construct. So the task accesses the firstprivate copy of j in the sections construct, which (in some implementations, including the Sun Studio compilers) is a local variable on the stack of the outlined routine for the sections construct.
Task execution may deferred so the task is executed at the implicit barrier at the end of the sections region, after the outlined routine for the sections construct has exited. So when the task references j, it accesses some undetermined value on the stack.
For correct results, the programmer needs to make sure that the task is executed before the sections region reaches its implicit barrier. This can be done by inserting a taskwait directive after the task construct. Alternatively, j can be specified to be firstprivate in the task construct, instead of shared.
#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); } } } } printf("After parallel, j = %d\n",j); } |
#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 } } } printf("After parallel, j = %d\n",j); } |