Auto-Detection of Thread Creation and Critical Section in Polyspace
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.
Multitasking Routines that Polyspace Can Detect
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.
POSIX
Thread creation:
pthread_create
Critical section begins:
pthread_mutex_lock
Critical section ends:
pthread_mutex_unlock
VxWorks
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 in Polyspace Desktop User Interface Using Configuration Template. 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, setvxworks_entry_point=main
.
Windows
Thread creation:
CreateThread
Critical section begins:
EnterCriticalSection
Critical section ends:
LeaveCriticalSection
μC/OS II
Thread creation:
OSTaskCreate
Critical section begins:
OSMutexPend
Critical section ends:
OSMutexPost
C++11
Thread creation:
std::thread::thread
Critical section begins: std::mutex::lock
or std::mutex::try_lock
Critical section ends:
std::mutex::unlock
Signal handler:
std::signal
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
.
C11
Thread creation:
thrd_create
Critical section begins:
mtx_lock
Critical section ends:
mtx_unlock
Signal handler:
signal
Example of Automatic Thread Detection
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;
}
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.
Naming Convention for Automatically Detected Threads
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
id
id
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); } }
Limitations of Automatic Thread Detection
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
andthrd_join
. Polyspace replacespthread_exit
andthrd_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
whenthreadId
is set toNULL
— When you create multiple threads that execute the same function, if the last argument ofCreateThread
isNULL
, 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.
See Also
Enable automatic
concurrency detection for Code Prover
(-enable-concurrency-detection)
| -code-behavior-specifications