Main Content

Double lock

Lock function is called twice in a task without an intermediate call to unlock function

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

This defect occurs when:

  • A task calls a lock function my_lock.

  • The task calls my_lock again before calling the corresponding unlock function.

In multitasking code, a lock function begins a critical section of code and an unlock function ends it. When a task task1 calls a lock function lock, other tasks calling lock must wait until task calls the corresponding unlock function.

To find this defect, specify your lock and unlock functions using one of these methods:

Risk

A call to a lock function begins a critical section so that other tasks have to wait to enter the same critical section. If the same lock function is called again within the critical section, the task blocks itself.

Fix

The fix depends on the root cause of the defect. A double lock defect often indicates a coding error. Perhaps you omitted the call to an unlock function to end a previous critical section and started the next critical section. Perhaps you wanted to use a different lock function for the second critical section.

Identify each critical section of code, that is, the section that you want to be executed as an atomic block. Call a lock function at the beginning of the section. Within the critical section, make sure that you do not call the lock function again. At the end of the section, call the unlock function that corresponds to the lock function.

See examples of fixes below. To avoid the issue, you can follow the practice of calling the lock and unlock functions in the same module at the same level of abstraction. For instance, in this example, func calls the lock and unlock function at the same level but func2 does not.

void func() {
  my_lock();
  {
    ...
  }
  my_unlock();
}

void func2() {
  {
   my_lock();
   ...
  }
  my_unlock();
}

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Extend Checker

You might be using locking functions that are not supported by Polyspace. Extend this checker by mapping your locking functions to its known POSIX® equivalent. See Extend Concurrency Defect Checkers to Unsupported Multithreading Environments.

Examples

expand all



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    lock(); 
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

In this example, to emulate multitasking behavior, you must specify the following options:

On the command-line, you can use the following:

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin lock:cs1
   -critical-section-end unlock:cs1

task1 enters a critical section through the call lock();. task1 calls lock again before it leaves the critical section through the call unlock();.

Correction — Remove First Lock

If you want the first global_var+=1; to be outside the critical section, one possible correction is to remove the first call to lock. However, if other tasks are using global_var, this code can produce a Data race error.



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    global_var += 1;
    lock(); 
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}
Correction — Remove Second Lock

If you want the first global_var+=1; to be inside the critical section, one possible correction is to remove the second call to lock.



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}
Correction — Add Another Unlock

If you want the second global_var+=1; to be inside a critical section, another possible correction is to add another call to unlock.



int global_var;

void lock(void);
void unlock(void);

void task1(void)
{
    lock();
    global_var += 1;
    unlock();
    lock();
    global_var += 1;
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}


int global_var;

void lock(void);
void unlock(void);

void performOperation(void) {
  lock();
  global_var++;
}

void task1(void)
{
    lock();
    global_var += 1;
    performOperation();
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

In this example, to emulate multitasking behavior, you must specify the following options:

On the command-line, you can use the following:

 polyspace-bug-finder
   -entry-points task1,task2
   -critical-section-begin lock:cs1
   -critical-section-end unlock:cs1

task1 enters a critical section through the call lock();. task1 calls the function performOperation. In performOperation, lock is called again even though task1 has not left the critical section through the call unlock();.

In the result details for the defect, you see the sequence of instructions leading to the defect. For instance, you see that following the first entry into the critical section, the execution path:

  • Enters function performOperation.

  • Inside performOperation, attempts to enter the same critical section once again.

You can click each event to navigate to the corresponding line in the source code.

Correction — Remove Second Lock

One possible correction is to remove the call to lock in task1.



int global_var;

void lock(void);
void unlock(void);

void performOperation(void) {
  global_var++;
}

void task1(void)
{
    lock();
    global_var += 1;
    performOperation();
    unlock();
}

void task2(void)
{
    lock(); 
    global_var += 1;
    unlock();
}

Result Information

Group: Concurrency
Language: C | C++
Default: On
Command-Line Syntax: DOUBLE_LOCK
Impact: High

Version History

Introduced in R2014b

expand all