Sun Studio 12: Thread Analyzer User's Guide

2.5 False Positives

Occasionally, the Thread Analyzer may report data-races that have not actually occurred in the program. These are called false positives. In most cases, false positives are caused by 2.5.1 User-Defined Synchronizations or 2.5.2 Memory That is Recycled by Different Threads.

2.5.1 User-Defined Synchronizations

The Thread Analyzer can recognize most standard synchronization APIs and constructs provided by OpenMP, POSIX threads, and Solaris threads. However, the tool cannot recognize user-defined synchronizations, and may report false data-races if your code contains such synchronizations. For example, the tool cannot recognize implementation of locks using CAS instructions, post and wait operations using busy-waits, etc. Here is a typical example of a class of false positives where the program employs a common way of using POSIX thread condition variables:

/* Initially ready_flag is 0 */
 
/* Thread 1: Producer */
100   data = ...
101   pthread_mutex_lock (&mutex);  
102   ready_flag = 1;
103   pthread_cond_signal (&cond);
104   pthread_mutex_unlock (&mutex);
...
/* Thread 2: Consumer */
200   pthread_mutex_lock (&mutex);
201   while (!ready_flag) {
202       pthread_cond_wait (&cond, &mutex);   
203   }
204   pthread_mutex_unlock (&mutex);
205   ... = data;

The pthread_cond_wait() call is usually made within a loop that tests the predicate to protect against program errors and spurious wake-ups. The test and set of the predicate is often protected by a mutex lock. In the above code, Thread 1 produces the value for the variable data at line 100, sets the value of ready_flag to one at line 102 to indicate that the data has been produced, and then calls pthread_cond_signal() to wake up the consumer thread, Thread 2. Thread 2 tests the predicate (!ready_flag) in a loop. When it finds that the flag is set, it consumes the data at line 205.

The write of ready_flag at line 102 and read of ready_flag at line 201 are protected by the same mutex lock, so there is no data-race between the two accesses and the tool recognizes that correctly.

The write of data at line 100 and the read of data at line 205 are not protected by mutex locks. However, in the program logic, the read at line 205 always happens after the write at line 100 because of the flag variable ready_flag. Consequently, there is no data-race between these two accesses to data. However, the tool reports that there is a data-race between the two accesses if the call to pthread_cond_wait() (line 202) is actually not called at run time. If line 102 is executed before line 201 is ever executed, then when line 201 is executed, the loop entry test fails and line 202 is skipped. The tool monitors pthread_cond_signal() calls and pthread_cond_wait() calls and can pair them to derive synchronization. When the pthread_cond_wait() at line 202 is not called, the tool does not know that the write at line 100 is always executed before the read at line 205. Therefore, it considers them as executed concurrently and reports a data-race between them.

In order to avoid reporting this kind of false positive data-race, the Thread Analyzer provides a set of APIs that can be used to notify the tool when user-defined synchronizations are performed. See A.1 The Thread-Analyzer's User-APIs for more information.

2.5.2 Memory That is Recycled by Different Threads

Some memory management routines recycle memory that is freed by one thread for use by another thread. The Thread Analyzer is sometimes not able to recognize that the life span of the same memory location used by different threads do not overlap. When this happens, the tool may report a false positive data-race. The following example illustrates this kind of false positive.

   /*----------*/                         /*----------*/
    /* Thread 1 */                         /* Thread 2 */
    /*----------*/                         /*----------*/
    ptr1 = mymalloc(sizeof(data_t));
    ptr1->data = ...
    ...
    myfree(ptr1);

                                           ptr2 = mymalloc(sizeof(data_t));   
                                           ptr2->data = ...
                                           ...
                                           myfree(ptr2);

Thread 1 and Thread 2 execute concurrently. Each thread allocates a chunk of memory that is used as its private memory. The routine mymalloc() may supply the memory freed by a previous call tomyfree(). If Thread 2 calls mymalloc() before Thread 1 calls myfree(), then ptr1 and ptr2 get different values and there is no data-race between the two threads. However, if Thread 2 calls mymalloc() after Thread 1 calls myfree(), then ptr1 and ptr2 may have the same value. There is no data-race because Thread 1 no longer accesses that memory. However, if the tool does not know mymalloc() is recycling memory, it reports a data-race between the write of ptr1 data and the write of ptr2 data. This kind of false positive often happens in C++ applications when the C++ runtime library recycles memory for temporary variables. It also often happens in user applications that implement their own memory management routines. Currently, the Thread Analyzer is able to recognize memory allocation and free operations performed with the standard malloc(), calloc(), and realloc() interfaces.