Main Content

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:

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:

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.

See also Limitations of Automatic Thread Detection.

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;
}
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.

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_id, where 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 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.

     Example

  • (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.

     Example

  • (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.

     Example

  • 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.

     Example

  • (C++11 only) If you use lambda expressions as start functions during thread creation, Polyspace does not detect shared variables in the lambda expressions.

     Example

  • (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.

     Example

See Also

|

Related Topics