Main Content

Function that can spuriously fail not wrapped in loop

Loop checks failure condition after possible spurious failure

Description

This defect occurs when the following atomic compare and exchange functions that can fail spuriously are called from outside a loop.

  • C atomic functions:

    • atomic_compare_exchange_weak()

    • atomic_compare_exchange_weak_explicit()

  • C++ atomic functions:

    • std::atomic<T>::compare_exchange_weak(T* expected, T desired)

    • std::atomic<T>::compare_exchange_weak_explicit(T* expected, T desired, std::memory_order succ, std::memory_order fail)

    • std::atomic_compare_exchange_weak(std::atomic<T>* obj, T* expected, T desired)

    • std::atomic_compare_exchange_weak_explicit(volatile std::atomic<T>* obj, T* expected, T desired, std::memory_order succ, std::memory_order fail)

The functions compare the memory contents of the object representations pointed to by obj and expected. The comparison can spuriously return false even if the memory contents are equal. This spurious failure makes the functions faster on some platforms.

Risk

An atomic compare and exchange function that spuriously fails can cause unexpected results and unexpected control flow.

Fix

Wrap atomic compare and exchange functions that can spuriously fail in a loop. The loop checks the failure condition after a possible spurious failure.

Examples

expand all

#include <stdatomic.h>

extern void reset_count(void);
atomic_int count = ATOMIC_VAR_INIT(0);

void increment_count(void)
{
    int old_count = atomic_load(&count);
    int new_count;
    new_count = old_count + 1;
    if (!atomic_compare_exchange_weak(&count, &old_count, new_count))
        reset_count();

}

In this example, increment_count() uses atomic_compare_exchange_weak() to compare count and old_count. If the counts are equal, count is incremented to new_count. If they are not equal, the count is reset. When atomic_compare_exchange_weak() fails spuriously, the count is reset unnecessarily.

Correction — Wrap atomic_compare_exchange_weak() in a while Loop

One possible correction is to wrap the call to atomic_compare_exchange_weak() in a while loop. The loop checks the failure condition after a possible spurious failure.

#include <stdatomic.h>

extern void reset_count(void);
atomic_int count = ATOMIC_VAR_INIT(0);

void increment_count(void)
{
    int old_count = atomic_load(&count);
    int new_count;
    new_count = old_count + 1;

    do {
        reset_count();

    } while (!atomic_compare_exchange_weak(&count, &old_count, new_count));

}

Result Information

Group: Concurrency
Language: C | C++
Default: Off
Command-Line Syntax: SPURIOUS_FAILURE_NOT_WRAPPED_IN_LOOP
Impact: Low

Version History

Introduced in R2018b