IssueData race occurs when:
To find this defect, you must specify the multitasking
options before analysis. To specify these options, on the
Configuration pane, select Multitasking. For
more information, see Configuring Polyspace Multitasking Analysis Manually.
RiskData race can result in unpredictable values of the shared variable
because you do not control the order of the operations in different
tasks.
Data races between two write
operations are more serious than data races between a write and read operation. Two write
operations can interfere with each other and result in indeterminate values. To identify
write-write conflicts, use the filters on the Detail column of the
Results List pane. For these conflicts, the
Detail column shows the additional
line:
Variable value may be altered by write-write concurrent access.
See
Filter and Group Results in Polyspace Desktop User Interface or
Filter and Sort Results in Polyspace Access Web Interface (Polyspace Access).
FixTo fix this defect, protect the
operations on the shared variable using critical sections, temporal exclusion or another
means. See Protections for Shared Variables in Multitasking Code.
To identify existing protections that you can reuse, see the
table and graphs associated with the result. The table shows each
pair of conflicting calls. The Access Protections column
shows existing protections on the calls. To see the function call
sequence leading to the conflicts, click the icon. For an example,
see below.
Example - Unprotected Operation on Global Variable from Multiple Tasks
int var; //Noncompliant
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
In this example, to emulate multitasking behavior,
specify the following options:
On the command-line, you can use
the following:
polyspace-bug-finder
-entry-points task1,task2,task3
-critical-section-begin begin_critical_section:cs1
-critical-section-end end_critical_section:cs1
In this example, the tasks task1
, task2
,
and task3
call the function increment
. increment
contains
the operation var++
that can involve multiple machine
instructions including:
These machine instructions, when executed from task1
and task2
,
can occur concurrently in an unpredictable sequence. For example,
reading var
from task1
can occur
either before or after writing to var
from task2
.
Therefore the value of var
can be unpredictable.
Though task3
calls increment
inside
a critical section, other tasks do not use the same critical section.
The operations in the critical section of task3
are
not mutually exclusive with operations in other tasks.
Therefore, the three tasks are operating on a shared variable
without common protection. In your result details, you see each pair
of conflicting function calls.
If you click the icon, you see the function
call sequence starting from the entry point to the read or write operation.
You also see that the operation starting from task3
is
in a critical section. The Access Protections entry
shows the lock and unlock function that begin and end the critical
section. In this example, you see the functions begin_critical_section
and end_critical_section
.
Correction — Place Operation in Critical SectionOne possible correction is to place the operation in critical
section. You can implement the critical section in multiple ways.
For instance:
You can place var++
in a critical
section. When task1
enters its critical section,
the other tasks cannot enter their critical sections until task1
leaves
its critical section. The operation var++
from
the three tasks cannot interfere with each other.
To implement the critical section, in the function increment
,
place the operation var++
between calls to begin_critical_section
and end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
begin_critical_section();
var++;
end_critical_section();
}
void task1(void) {
increment();
}
void task2(void) {
increment();
}
void task3(void) {
increment();
}
You can place the call to increment
in
the same critical section in the three tasks. When task1
enters
its critical section, the other tasks cannot enter their critical
sections until task1
leaves its critical section.
The calls to increment
from the three tasks cannot
interfere with each other.
To implement the critical section, in each of the three tasks,
call increment
between calls to begin_critical_section
and end_critical_section
.
int var;
void begin_critical_section(void);
void end_critical_section(void);
void increment(void) {
var++;
}
void task1(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task2(void) {
begin_critical_section();
increment();
end_critical_section();
}
void task3(void) {
begin_critical_section();
increment();
end_critical_section();
}
Correction — Make Tasks Temporally ExclusiveAnother possible correction is to make the tasks, task1
, task2
and task3
,
temporally exclusive. Temporally exclusive tasks cannot execute concurrently.
On the Configuration pane, specify
the following additional options:
On the command-line, you can use
the following:
polyspace-bug-finder
-temporal-exclusions-file "C:\exclusions_file.txt"
where the file
C:\exclusions_file.txt
has the
following line:
Example - Unprotected Operation in Threads Created with
pthread_create
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count; //Noncompliant
void* increment_count(void* args)
{
count = count + 1;
return NULL;
}
void* set_count(void *args)
{
long long c;
c = count;
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}
In this example, Bug Finder detects
the creation of separate threads with pthread_create
. The Data
race defect is raised because the operation count = count +
1
in the thread with id thread_increment
conflicts with the
operation c = count
in the thread with id thread_get
.
The variable count
is accessed in multiple threads without a common
protection.
The two conflicting operations are
nonatomic. The operation c = count
is nonatomic on 32-bit targets. See
Define Atomic Operations in Multitasking Code.
Correction — Protect Operations with
pthread_mutex_lock
and
pthread_mutex_unlock
PairTo prevent concurrent access on the variable count
,
protect operations on count
with a critical
section. Use the functions pthread_mutex_lock
and
pthread_mutex_unlock
to implement the
critical section.
#include <pthread.h>
pthread_mutex_t count_mutex;
long long count;
void* increment_count(void* args)
{
pthread_mutex_lock(&count_mutex);
count = count + 1;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
void* set_count(void *args)
{
long long c;
pthread_mutex_lock(&count_mutex);
c = count;
pthread_mutex_unlock(&count_mutex);
return NULL;
}
int main(void)
{
pthread_t thread_increment;
pthread_t thread_get;
pthread_create(&thread_increment, NULL, increment_count, NULL);
pthread_create(&thread_get, NULL, set_count, NULL);
pthread_join(thread_get, NULL);
pthread_join(thread_increment, NULL);
return 1;
}