JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Oracle Solaris Studio 12.3: Thread Analyzer User's Guide     Oracle Solaris Studio 12.3 Information Library
search filter icon
search icon

Document Information

Preface

1.  What is the Thread Analyzer and What Does It Do?

2.  The Data Race Tutorial

2.1 Data Race Tutorial Source Files

2.1.1 Getting the Data Race Tutorial Source Files

2.1.2 Source Code for prime_omp.c

2.1.3 Source Code for prime_pthr.c

2.1.3.1 Effect of Data Races in prime_omp.c and prime_pthr.c

2.2 How to Use the Thread Analyzer to Find Data Races

2.2.1 Instrument the Code

2.2.1.1 To Instrument Source Code

2.2.1.2 To Instrument Binary Code

2.2.2 Create a Data Race Detection Experiment

2.2.3 Examine the Data Race Detection Experiment

2.2.3.1 Using Thread Analyzer to View the Data Race Experiment

2.2.3.2 Using er_print to View the Data Race Experiment

2.3 Understanding the Experiment Results

2.3.1 Data Races in prime_omp.c

2.3.2 Data Races in prime_pthr.c

2.3.3 Call Stack Traces of Data Races

2.4 Diagnosing the Cause of a Data Race

2.4.1 Check Whether or Not the Data Race is a False Positive

2.4.2 Check Whether or Not the Data Race is Benign

2.4.3 Fix the Bug, Not the Data Race

2.4.3.1 Fixing Bugs in prime_omp.c

2.4.3.2 Fixing Bugs in prime_pthr.c

2.5 False Positives

2.5.1 User-Defined Synchronizations

2.5.2 Memory That is Recycled by Different Threads

2.6 Benign Data Races

2.6.1 A Program for Finding Primes

2.6.2 A Program that Verifies Array-Value Types

2.6.3 A Program Using Double-Checked Locking

3.  The Deadlock Tutorial

A.  APIs Recognized by the Thread Analyzer

B.  Useful Tips

2.4 Diagnosing the Cause of a Data Race

This section provides a basic strategy to diagnosing the cause of data races.

2.4.1 Check Whether or Not the Data Race is a False Positive

A false positive data race is a data race that is reported by the Thread Analyzer, but has actually not occurred. The Thread Analyzer tries to reduce the number of false positives reported. However, there are cases where the tool is not able to do a precise job and may report false positive data races.

You can ignore a false positive data race because it is not a genuine data race and, therefore, does not affect the behavior of the program.

See 2.5 False Positives for some examples of false positive data races. For information on how to remove false positive data races from the report, see A.1 Thread Analyzer User APIs.

2.4.2 Check Whether or Not the Data Race is Benign

A benign data race is an intentional data race whose existence does not affect the correctness of the program.

Some multithreaded applications intentionally use code that may cause data races. Since the data races are there by design, no fix is required. In some cases, however, it is quite tricky to get such codes to run correctly. These data races should be reviewed carefully.

See 2.5 False Positives for more detailed information about benign races.

2.4.3 Fix the Bug, Not the Data Race

The Thread Analyzer can help find data races in the program, but it cannot automatically find bugs in the program nor suggest ways to fix the data races found. A data race may have been introduced by a bug. It is important to find and fix the bug. Merely removing the data race is not the right approach, and could make further debugging even more difficult.

2.4.3.1 Fixing Bugs in prime_omp.c

Here's how to fix the bug in prime_omp.c. See 2.1.2 Source Code for prime_omp.c for a complete file listing.

Move lines 50 and 51 into a critical section in order to remove the data race on elements of the array primes[ ].

47      #pragma omp parallel for
48      for (i = 2; i < N; i++) {
49          if ( is_prime(i) ) {
                 #pragma omp critical

                 {
50                  primes[total] = i;
51                  total++;
                 }
52          }
53     }

You could also move lines 50 and 51 into two critical sections as follows, but this change fails to correct the program:

47      #pragma omp parallel for
48      for (i = 2; i < N; i++) {
49          if ( is_prime(i) ) {
                 #pragma omp critical
                 {
50                  primes[total] = i;
                 }
                 #pragma omp critical
                 {
51                  total++;
                 }
52          }
53     }

The critical sections around lines 50 and 51 get rid of the data race because the threads are using exclusive locks to control their accesses to the primes[ ] array. However, the program is still incorrect. Two threads might update the same element of primes[ ] using the same value of total, and some elements of primes[ ] may not be assigned a value at all.

The second data race, between a read from pflag[ ] from line 23 and a write to pflag[ ] from line 26, is actually a benign race because it does not lead to incorrect results. It is not essential to fix benign data races.

2.4.3.2 Fixing Bugs in prime_pthr.c

Here's how to fix the bug in prime_pthr.c. See 2.1.3 Source Code for prime_pthr.c for a complete file listing.

Use a single mutex to remove the data race on prime[ ] at line 44, as well as the data race on total at line 45.

The data race between the write to i on line 60 and the read from the same memory location (named *arg) on line 40, as well as the data race on pflag[ ] on line 27, reveal a problem in the shared access to the variable i by different threads. The initial thread in prime_pthr.c creates the child threads in a loop in lines 60-62, and dispatches them to work on the function work(). The loop index i is passed to work() by address. Since all threads access the same memory location for i, the value of i for each thread will not remain unique, but will change as the initial thread increments the loop index. As different threads use the same value of i, the data races occur. One way to fix the problem is to pass i to work() by value, instead of by address.

Here is the corrected version of prime_pthr.c:

  1  /*
  2   * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
  3   * @(#)prime_pthr_fixed.c 1.3 (Oracle) 10/03/26
  4   */
  5
  6  #include <stdio.h>
  7  #include <math.h>
  8  #include <pthread.h>
  9
 10  #define THREADS 4
 11  #define N 10000
 12
 13  int primes[N];
 14  int pflag[N];
 15  int total = 0;
 16  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 17
 18  int is_prime(int v)
 19  {
 20      int i;
 21      int bound = floor(sqrt(v)) + 1;
 22
 23      for (i = 2; i < bound; i++) {
 24          /* no need to check against known composites */
 25          if (!pflag[i])
 26              continue;
 27          if (v % i == 0) {
 28              pflag[v] = 0;
 29              return 0;
 30          }
 31      }
 32      return (v > 1);
 33  }
 34
 35  void *work(void *arg)
 36  {
 37      int start;
 38      int end;
 39      int i;
 40
 41      start = (N/THREADS) * ((int)arg) ;
 42      end = start + N/THREADS;
 43      for (i = start; i < end; i++) {
 44          if ( is_prime(i) ) {
 45              pthread_mutex_lock(&mutex);
 46              primes[total] = i;
 47              total++;
 48              pthread_mutex_unlock(&mutex);
 49          }
 50      }
 51      return NULL;
 52  }
 53
 54  int main(int argn, char **argv)
 55  {
 56      int i;
 57      pthread_t tids[THREADS-1];
 58
 59      for (i = 0; i < N; i++) {
 60          pflag[i] = 1;
 61      }
 62
 63      for (i = 0; i < THREADS-1; i++) {
 64          pthread_create(&tids[i], NULL, work, (void *)i);
 65      }
 66
 67      i = THREADS-1;
 68      work((void *)i);
 69
 70      for (i = 0; i < THREADS-1; i++) {
 71          pthread_join(tids[i], NULL);
 72      }
 73
 74      printf("Number of prime numbers between 2 and %d: %d\n",
 75             N, total);
 76
 77      return 0;
 78  }