With Polyspace®, you can analyze programs where multiple threads run concurrently.
Polyspace can analyze your multitasking code for data races, deadlocks and other
concurrency defects, if the analysis is aware of the concurrency model in your code. In
some situations, Polyspace can detect thread creation and critical sections in your code
automatically. Bug Finder detects them by default. In Code Prover, you enable automatic
detection using the option Enable automatic concurrency detection
for Code Prover (-enable-concurrency-detection).
For the multitasking code analysis workflow, see Analyze Multitasking Programs in Polyspace.
If your thread creation function is not detected automatically:
You can also map the function to a thread-creation function that
Polyspace can detect automatically. Use the option -code-behavior-specifications.
Otherwise, you must manually model your multitasking threads by using configuration options. See Configuring Polyspace Multitasking Analysis Manually.
Polyspace can detect thread creation and critical sections if you use primitives from these groups. Polyspace recognizes calls to these routines as the creation of a new thread or as the beginning or end of a critical section.
Thread creation:
pthread_create
Critical section begins:
pthread_mutex_lock
Critical section ends:
pthread_mutex_unlock
![]()
Thread creation:
taskSpawn
Critical section begins:
semTake
Critical section ends:
semGive
To activate automatic detection of concurrency primitives for VxWorks®, in the user interface of the Polyspace desktop products, use the VxWorks template. For
more information on templates, see Create Project Using Configuration Template (Polyspace Code Prover). At the command-line, use
these
options:
-D1=CPU=I80386 -D2=__GNUC__=2 -D3=__OS_VXWORKS
Concurrency detection is possible only if the multitasking functions are
created from an entry point named main. If the entry point
has a different name, such as vxworks_entry_point, do one of
the following:
Provide a main function.
Preprocessor
definitions (-D): In preprocessor definitions, set
vxworks_entry_point=main.
![]()
Thread creation:
CreateThread
Critical section begins:
EnterCriticalSection
Critical section ends:
LeaveCriticalSection
![]()
Thread creation:
OSTaskCreate
Critical section begins:
OSMutexPend
Critical section ends:
OSMutexPost
![]()
Thread creation:
std::thread::thread
Critical section begins:
std::mutex::lock
Critical section ends:
std::mutex::unlock
For autodetection of C++11 threads, explicitly specify paths to your compiler
header files or use polyspace-configure.
For instance, if you use std::thread for thread creation,
explicitly specify the path to the folder containing
thread.h.
See also Limitations of Automatic Thread Detection.
![]()
Thread creation:
thrd_create
Critical section begins:
mtx_lock
Critical section ends:
mtx_unlock
![]()
The following multitasking code models five philosophers sharing five forks. The example uses POSIX® thread creation routines and illustrates a classic example of a deadlock. Run Bug Finder on this code to see the deadlock.
#include "pthread.h"
#include <stdio.h>
#include <unistd.h>
pthread_mutex_t forks[5];
void* philo1(void* args)
{
while (1) {
printf("Philosopher 1 is thinking\n");
sleep(1);
pthread_mutex_lock(&forks[0]);
printf("Philosopher 1 takes left fork\n");
pthread_mutex_lock(&forks[1]);
printf("Philosopher 1 takes right fork\n");
printf("Philosopher 1 is eating\n");
sleep(1);
pthread_mutex_unlock(&forks[1]);
printf("Philosopher 1 puts down right fork\n");
pthread_mutex_unlock(&forks[0]);
printf("Philosopher 1 puts down left fork\n");
}
return NULL;
}
void* philo2(void* args)
{
while (1) {
printf("Philosopher 2 is thinking\n");
sleep(1);
pthread_mutex_lock(&forks[1]);
printf("Philosopher 2 takes left fork\n");
pthread_mutex_lock(&forks[2]);
printf("Philosopher 2 takes right fork\n");
printf("Philosopher 2 is eating\n");
sleep(1);
pthread_mutex_unlock(&forks[2]);
printf("Philosopher 2 puts down right fork\n");
pthread_mutex_unlock(&forks[1]);
printf("Philosopher 2 puts down left fork\n");
}
return NULL;
}
void* philo3(void* args)
{
while (1) {
printf("Philosopher 3 is thinking\n");
sleep(1);
pthread_mutex_lock(&forks[2]);
printf("Philosopher 3 takes left fork\n");
pthread_mutex_lock(&forks[3]);
printf("Philosopher 3 takes right fork\n");
printf("Philosopher 3 is eating\n");
sleep(1);
pthread_mutex_unlock(&forks[3]);
printf("Philosopher 3 puts down right fork\n");
pthread_mutex_unlock(&forks[2]);
printf("Philosopher 3 puts down left fork\n");
}
return NULL;
}
void* philo4(void* args)
{
while (1) {
printf("Philosopher 4 is thinking\n");
sleep(1);
pthread_mutex_lock(&forks[3]);
printf("Philosopher 4 takes left fork\n");
pthread_mutex_lock(&forks[4]);
printf("Philosopher 4 takes right fork\n");
printf("Philosopher 4 is eating\n");
sleep(1);
pthread_mutex_unlock(&forks[4]);
printf("Philosopher 4 puts down right fork\n");
pthread_mutex_unlock(&forks[3]);
printf("Philosopher 4 puts down left fork\n");
}
return NULL;
}
void* philo5(void* args)
{
while (1) {
printf("Philosopher 5 is thinking\n");
sleep(1);
pthread_mutex_lock(&forks[4]);
printf("Philosopher 5 takes left fork\n");
pthread_mutex_lock(&forks[0]);
printf("Philosopher 5 takes right fork\n");
printf("Philosopher 5 is eating\n");
sleep(1);
pthread_mutex_unlock(&forks[0]);
printf("Philosopher 5 puts down right fork\n");
pthread_mutex_unlock(&forks[4]);
printf("Philosopher 5 puts down left fork\n");
}
return NULL;
}
int main(void)
{
pthread_t ph[5];
pthread_create(&ph[0], NULL, philo1, NULL);
pthread_create(&ph[1], NULL, philo2, NULL);
pthread_create(&ph[2], NULL, philo3, NULL);
pthread_create(&ph[3], NULL, philo4, NULL);
pthread_create(&ph[4], NULL, philo5, NULL);
pthread_join(ph[0], NULL);
pthread_join(ph[1], NULL);
pthread_join(ph[2], NULL);
pthread_join(ph[3], NULL);
pthread_join(ph[4], NULL);
return 1;
} |
Each philosopher needs two forks to eat, a right and a left fork.
The functions philo1, philo2,
philo3, philo4, and
philo5 represent the philosophers. Each function requires two
pthread_mutex_t resources, representing the two forks
required to eat. All five functions run at the same
time in five concurrent threads.
However, a deadlock occurs in this example. When each philosopher picks up their
first fork (each thread locks one pthread_mutex_t resource),
all the forks are being used. So, the philosophers
(threads) wait for their second fork (second pthread_mutex_t
resource) to become available. However, all the
forks (resources) are being held by the waiting philosophers (threads), causing a
deadlock.
![]()
If you use a function such as pthread_create() to create new
threads (tasks), each thread is associated with an unique identifier. For instance,
in this example, two threads are created with identifiers id1 and
id2.
pthread_t* id1, id2;
void main()
{
pthread_create(id1, NULL, start_routine, NULL);
pthread_create(id2, NULL, start_routine, NULL);
} |
If a data race occurs between the threads, the analysis can detect it. When
displaying the results, the threads are indicated as
task_, where
idid is the identifier associated with the thread. In the
preceding example, the threads are identified as task_id1 and
task_id2.
If a thread identifier is:
Local to a function, the thread name shows the function.
For instance, the thread created below appears as
task_f:id
void f(void)
{
pthread_t* id;
pthread_create(id, NULL, start_routine, NULL);
} |
A field of a structure, the thread name shows the structure.
For instance, the thread created below appears as
task_a#id
struct {pthread_t* id; int x;} a;
pthread_create(a.id,NULL,start_routine,NULL);
|
An array member, the thread name shows the array.
For instance, the thread created below appears as
task_tab[1].
pthread_t* tab[10]; pthread_create(tab[1],NULL,start_routine,NULL); |
If you create two threads with distinct thread identifiers, but you
use the same local variable name for the thread identifiers, the name of the second
thread is modified to distinguish it from the first thread. For instance, the
threads below appear as task_func:id and
task_func:id:1.
void func()
{
{
pthread_t id;
pthread_create(&id, NULL, &task, NULL);
}
{
pthread_t id;
pthread_create(&id, NULL, &task, NULL);
}
} |
![]()
The multitasking model extracted by Polyspace does not include some features. Polyspace cannot model:
Thread priorities and attributes — Ignored by Polyspace.
Recursive semaphores.
Unbounded thread identifiers, such as extern pthread_t
ids[] — Warning.
Calls to concurrency primitive through high-order calls — Warning.
Aliases on thread identifiers — Polyspace over-approximates when the alias is used.
Termination of threads — Polyspace ignores pthread_join and
thrd_join. Polyspace replaces pthread_exit and
thrd_exit by a standard exit.
(Polyspace Bug Finder™ only) Creation of multiple threads through multiple calls to the same function with different pointer arguments.
(Polyspace Code Prover™ only) Shared local variables — Only global variables are considered shared. If a local variable is accessed by multiple threads, the analysis does not take into account the shared nature of the variable.
(Polyspace Code Prover only) Shared dynamic memory — Only global variables are considered shared. If a dynamically allocated memory region is accessed by multiple threads, the analysis does not take into account its shared nature.
Number of tasks created with CreateThread when
threadId is set to
NULL— When you create multiple threads that
execute the same function, if the last argument of
CreateThread is NULL,
Polyspace only detects one instance of this function, or
task.
(C++11 only) If you use lambda expressions as start functions during thread creation, Polyspace does not detect shared variables in the lambda expressions.
(C++11 threads with Polyspace
Code Prover only) String literals as thread function argument —
Code Prover shows a red Illegally dereferenced
pointer error if the thread function has an
std::string& parameter and you pass a string
literal argument.
-code-behavior-specifications | Enable automatic concurrency
detection for Code Prover (-enable-concurrency-detection)