Go to main content
Oracle® Developer Studio 12.5: Thread Analyzer User's Guide

Exit Print View

Updated: June 2016
 
 

False Positives

Occasionally, Thread Analyzer might report data races that have not actually occurred in the program. These are called false positives. In most cases, false positives are caused by user-defined synchronizations or by memory that is recycled by different threads. For more information, see User-Defined Synchronizations and Memory That is Recycled by Different Threads.

User-Defined Synchronizations

Thread Analyzer can recognize most standard synchronization APIs and constructs provided by OpenMP, POSIX threads, and Oracle Solaris threads. However, the tool cannot recognize user-defined synchronizations, and might report false positive data races if your code contains such synchronizations.


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

To illustrate why you might need to use the APIs, consider the following. Thread Analyzer cannot recognize implementation of locks using CAS instructions, post and wait operations using busy-waits, and so on. 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.

The libtha(3C) man page and Thread Analyzer User APIs explain how to use the APIs to avoid reports of this kind of false positive data race.

Memory That is Recycled by Different Threads

Some memory management routines recycle memory that is freed by one thread for use by another thread. Thread Analyzer is sometimes not able to recognize that the life spans of the same memory location used by different threads do not overlap. When this happens, the tool might 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() might supply the memory freed by a previous call to myfree(). 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 might 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, Thread Analyzer is able to recognize memory allocation and free operations performed with the standard malloc(), calloc(), and realloc() interfaces.