Start here

Home
About Klocwork
What's new
Fixed issues
Release notes
Installation

Reference

C/C++ checkers
Java checkers
C# checkers
MISRA C 2004 checkers
MISRA C++ 2008 checkers
MISRA C 2012 checkers
MISRA C 2012 checkers with Amendment 1
Commands
Metrics
Troubleshooting
Reference

Product components

C/C++ Integration build analysis
Java Integration build analysis
Desktop analysis
Refactoring
Klocwork Static Code Analysis
Klocwork Code Review
Structure101
Tuning
Custom checkers

Coding environments

Visual Studio
Eclipse for C/C++
Eclipse for Java
IntelliJ IDEA
Other

Administration

Project configuration
Build configuration
Administration
Analysis performance
Server performance
Security/permissions
Licensing
Klocwork Static Code Analysis Web API
Klocwork Code Review Web API

Community

View help online
Visit RogueWave.com
Klocwork Support
Rogue Wave Videos

Legal

Legal information

CONC.DL

Deadlock

Deadlock in a multi-threaded program is a condition in which two or more threads mutually block, each waiting for the other to finish. This is typically caused by out-of-order lock contention, in which each thread holds a resource that another thread requires, and so none can unblock until it acquires the resource.

Deadlocks are typically caused by circular chains of locks that are reserved out of order. For example, Thread 1 executes code that has a lock reservation pattern that calls for lock A to be reserved before lock B, while Thread 2 executes (potentially the same) code, but instead has a lock reservation pattern that calls for lock B to be reserved before lock A. If these two threads collide in execution, it is possible that Thread 1 will be requesting lock B after Thread 2 has already reserved it. Thread 2 may request lock A, only to be blocked because Thread 1 has already reserved it.

Checker CONC.DL finds instances of deadlock.

Vulnerability and risk

This type of circular lock reference can cause stuck programs, inactive UIs, unresponsive devices, and similar problems. Debugging these issues in the field can easily consume weeks of developer time and customer frustration.

Mitigation and prevention

To help avoid lock contention:

  • Try to keep locked sections of code as small and as simple to understand as possible.
  • Don't lock sections of code that can cause concurrency problems, such as data races.
  • Avoid circular wait conditions at all costs.
  • If several locks are used, typically in an escalating guard pattern, make absolutely sure that the escalation is performed exactly the same in every circumstance.

Vulnerable code example 1

     1  #include <stdio.h>
     2  #include <pthread.h>
     3  static pthread_mutex_t A, B;
     4
     5  void *printmsg1(void *msg) {
     6      pthread_mutex_lock(&A);  // Execution step 1
     7      pthread_mutex_lock(&B);  // Execution step 3
     8      printf("printmsg1\n");
     9      pthread_mutex_unlock(&B);
    10      pthread_mutex_unlock(&A);
    11      return 0;
    12  }
    13
    14  void *printmsg2(void *msg) {
    15      pthread_mutex_lock(&B);  // Execution step 2
    16      pthread_mutex_lock(&A);
    17      printf("printmsg2\n");
    18      pthread_mutex_unlock(&A);
    19      pthread_mutex_unlock(&B);
    20      return 0;
    21  }
    22
    23  int main(int argc, char**argv) {
    24      pthread_t pt1, pt2;
    25      pthread_mutex_init(&A, NULL);
    26      pthread_mutex_init(&B, NULL);
    27      pthread_create(&pt1,0, printmsg1, NULL);
    28      pthread_create(&pt2,0, printmsg2, NULL);
    29      pthread_join(pt1,0);
    30      pthread_join(pt2,0);
    31      pthread_mutex_destroy(&A);
    32      pthread_mutex_destroy(&B);
    33      return 0;
    34  }

Klocwork reports a possible deadlock on line 6 between between the two threads using global mutexes 'A' and 'B'. The main routine creates and starts two threads, which are defined in functions 'printmsg1' and 'printmsg2'. The first thread waits to acquire the lock on mutex 'B' at line 7 while holding the lock on mutex 'A'. The second thread waits to acquire the lock on mutex 'A' at line 16 while holding the lock on mutex 'B'. This circular chain of locks between the two threads can be responsible for a deadlock.

Fixed code example 1

     1  #include <stdio.h>
     2  #include <pthread.h>
     3  static pthread_mutex_t A, B;
     4
     5  void *printmsg1(void *msg) {
     6      pthread_mutex_lock(&A);
     7      pthread_mutex_lock(&B);
     8      printf("printmsg1\n");
     9      pthread_mutex_unlock(&B);
    10      pthread_mutex_unlock(&A);
    11      return 0;
    12  }
    13
    14  void *printmsg2(void *msg) {
    15      pthread_mutex_lock(&A);
    16      pthread_mutex_lock(&B);
    17      printf("printmsg2\n");
    18      pthread_mutex_unlock(&B);
    19      pthread_mutex_unlock(&A);
    20      return 0;
    21  }
    22
    23  int main(int argc, char**argv) {
    24      pthread_t pt1, pt2;
    25      pthread_mutex_init(&A, NULL);
    26      pthread_mutex_init(&B, NULL);
    27      pthread_create(&pt1,0, printmsg1, NULL);
    28      pthread_create(&pt2,0, printmsg2, NULL);
    29      pthread_join(pt1,0);
    30      pthread_join(pt2,0);
    31      pthread_mutex_destroy(&A);
    32      pthread_mutex_destroy(&B);
    33      return 0;
    34  }

If several mutexes are used for synchronization in each process, mutexes should be locked in the same order. In the fixed code example, the order of the locks and unlocks is changed in lines 15, 16, 18, and 19 to be the same in 'printmsg2' as in 'printmsg1', ensuring that a circular chain doesn't occur.

Vulnerable code example 2

     1  #include <stdio.h>
     2  #include <pthread.h>
     3  static pthread_mutex_t A, B;
     4  static pthread_cond_t cond;
     5  static int count;
     6  
     7  void *f1(void *msg) {
     8      pthread_mutex_lock(&A);  
     9      pthread_mutex_lock(&B);
    10      while (count) {
    11          pthread_cond_wait(&cond, &B);
    12      }  
    13      pthread_mutex_unlock(&B);
    14      pthread_mutex_unlock(&A);
    15      return 0;
    16  }   
    17  void *f2(void *msg) {
    18      pthread_mutex_lock(&A);
    19      pthread_mutex_lock(&B);
    20      count--;
    21      pthread_cond_broadcast(&cond);
    22      pthread_mutex_unlock(&B);
    23      pthread_mutex_unlock(&A);
    24      return 0;
    25  }   
    26      
    27  int main(int argc, char**argv) {
    28      pthread_t pt1, pt2;
    29      pthread_mutex_init(&A, NULL);
    30      pthread_mutex_init(&B, NULL);
    31      pthread_cond_init(&cond, NULL);
    32      count = 1;
    33      pthread_create(&pt1,0, f1, NULL);
    34      pthread_create(&pt2,0, f2, NULL);
    35  
    36      pthread_join(pt1, NULL);
    37      pthread_join(pt2, NULL);
    38      pthread_mutex_destroy(&A);
    39      pthread_mutex_destroy(&B);
    40      pthread_cond_destroy(&cond);
    41      return 0;
    42  }

Klocwork reports a possible deadlock on line 11 between the two threads using global mutex 'A' and conditional variable 'cond'. The first thread may wait indefinitely for conditional variable 'cond' at line 11 while holding the lock on mutex 'A'. The second thread waits to acquire the lock on mutex 'A' at line 18 to change global variable 'count' and send a signal to conditional variable 'cond'.

Fixed code example 2

     1  #include <stdio.h>
     2  #include <pthread.h>
     3  static pthread_mutex_t A, B;
     4  static pthread_cond_t cond;
     5  static int count;
     6  
     7  void *f1(void *msg) {
     8      pthread_mutex_lock(&B);
     9      while (count) {
    10          pthread_cond_wait(&cond, &B);
    11      }  
    12      pthread_mutex_unlock(&B);
    13      return 0;
    14  }
    15  void *f2(void *msg) {
    16      pthread_mutex_lock(&A);
    17      pthread_mutex_lock(&B);
    18      count--;
    19      pthread_cond_broadcast(&cond);
    20      pthread_mutex_unlock(&B);
    21      pthread_mutex_unlock(&A);
    22      return 0;
    23  }
    24  
    25  int main(int argc, char**argv) {
    26      pthread_t pt1, pt2;
    27      pthread_mutex_init(&A, NULL);
    28      pthread_mutex_init(&B, NULL);
    29      pthread_cond_init(&cond, NULL);
    30      count = 1;
    31      pthread_create(&pt1,0, f1, NULL);
    32      pthread_create(&pt2,0, f2, NULL);
    33  
    34      pthread_join(pt1, NULL);
    35      pthread_join(pt2, NULL);
    36      pthread_mutex_destroy(&A);
    37      pthread_mutex_destroy(&B);
    38      pthread_cond_destroy(&cond);
    39      return 0;
    40  }

Before a conditional wait function, a lock set should contain only mutexes that are required and unlocked in the same function. In the fixed code example, the mutex 'A' lock before the wait function 'pthread_cond_wait' has been removed, since it's unnecessary. Removing the mutex 'A' lock eliminates the possibility of a deadlock report at line 10.

Related checkers

Extension

This checker can be extended. The related knowledge base record kinds are:

See Tuning C/C++ analysis for more information.