Main Content

CERT C++: CON40-C

Do not refer to an atomic variable twice in an expression

Description

Rule Definition

Do not refer to an atomic variable twice in an expression.1

Polyspace Implementation

The rule checker checks for these issues:

  • Atomic variable accessed twice in an expression.

  • Atomic load and store sequence not atomic.

Examples

expand all

Issue

Atomic variable accessed twice in an expression occurs when C atomic types or C++ std::atomic class variables appear twice in an expression and there are:

  • Two atomic read operations on the variable.

  • An atomic read and a distinct atomic write operation on the variable.

The C standard defines certain operations on atomic variables that are thread safe and do not cause data race conditions. Unlike individual operations, a pair of operations on the same atomic variable in an expression is not thread safe.

Risk

A thread can modify the atomic variable between the pair of atomic operations, which can result in a data race condition.

Fix

Do not reference an atomic variable twice in the same expression.

Example - Referencing Atomic Variable Twice in an Expression

To run this example, use these options:

  • -cpp-version cpp11

  • -compiler gnu4.9

#include <atomic>
std::atomic<int> n(5);
int compute_sum(void)
{
    return n * (n + 1) / 2; //Noncompliant
}

In this example, the global variable n is referenced twice in the return statement of compute_sum(). The value of n can change between the two distinct read operations. compute_sum() can return an incorrect value.

Correction — Pass Variable as Function Argument

One possible correction is to pass the variable as a function argument n. The variable is copied to memory and the read operations on the copy guarantee that compute_sum() returns a correct result. If you pass a variable of type int instead of type atomic_int, the correction is still valid.

#include <atomic>
int compute_sum(std::atomic<int> n)
{
    return n * (n + 1) / 2;
}
Issue

Atomic load and store sequence not atomic occurs when you use these functions to load, and then store an atomic variable.

  • C functions:

    • atomic_load()

    • atomic_load_explicit()

    • atomic_store()

    • atomic_store_explicit()

  • C++ functions:

    • std::atomic_load()

    • std::atomic_load_explicit()

    • std::atomic_store()

    • std::atomic_store_explicit()

    • std::atomic::load()

    • std::atomic::store()

A thread cannot interrupt an atomic load or an atomic store operation on a variable, but a thread can interrupt a store, and then load sequence.

Risk

A thread can modify a variable between the load and store operations, resulting in a data race condition.

Fix

To read, modify, and store a variable atomically, use a compound assignment operator such as +=, atomic_compare_exchange() or atomic_fetch_*-family functions.

Example - Loading Then Storing an Atomic Variable

To run this example, use these options:

  • -cpp-version cpp11

  • -compiler gnu4.9

#include <atomic>
#include <stdbool.h>
using namespace std;
static atomic<bool> flag(false);

void init_flag(void)
{
    atomic_init(&flag, false);
}

void toggle_flag(void)
{
    bool temp_flag = atomic_load(&flag);
    temp_flag = !temp_flag;
    atomic_store(&flag, temp_flag); //Noncompliant
}

bool get_flag(void)
{
    return atomic_load(&flag);
}

In this example, variable flag of type atomic_bool is referenced twice inside the toggle_flag() function. The function loads the variable, negates its value, then stores the new value back to the variable. If two threads call toggle_flag(), the second thread can access flag between the load and store operations of the first thread. flag can end up in an incorrect state.

Correction — Use Compound Assignment to Modify Variable

One possible correction is to use the function atomic_compare_exchange_weak to perform a safe and atomic compare-and-exchange. When you use this function, the changes to flag are visible to other threads, and the expected result is stored in flag.

#include <atomic>
#include <stdbool.h>
using namespace std;
static atomic<bool> flag(false);


void toggle_flag(void)
{
  bool old_flag = atomic_load(&flag);
  bool new_flag;
  do {
    new_flag = !old_flag;
  } while (!atomic_compare_exchange_weak(&flag, &old_flag, new_flag));

}

bool get_flag(void)
{
    return atomic_load(&flag);
}

Check Information

Group: 10. Concurrency (CON)

Version History

Introduced in R2019a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.