You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 32 Next »

Functions that can fail spuriously should be wrapped in a loop.  The atomic_compare_exchange_weak() and atomic_compare_exchange_weak_explicit() functions both attempt to set an atomic variable to a new value but only if it currently possesses a known old value. Unlike the related functions atomic_compare_exchange_strong() and atomic_compare_exchange_strong_explicit(), these functions are permitted to "fail spuriously," which makes them faster on some platforms. The C Standard describes this behavior in subclause 7.17.7.4, paragraph 4:

A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory referred to by expected and object are equal, it may return zero and store back to expected the same memory contents that were originally there.

Noncompliant Code Example

This noncompliant code example calls the atomic_compare_exchange_weak() outside of a loop .  Consequently it is possible for func() to complete without actually toggleing the value of cur.


#include <stdbool.h>
#include <stdatomic.h>
 
static atomic_bool cur;
 
void init_flag(void) {
  atomic_init(&cur, false);
}
 
void func(void) {
 init_flag();
 
 bool exp = atomic_load(&cur);
 bool des = !exp;
 
  if (!atomic_compare_exchange_weak(&cur, &exp, des)) {
    /* Handle error */
  }
}

Compliant Solution (atomic_compare_exchange_weak())

A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop. This compliant solution calls the atomic_compare_exchange_weak() function from within a while loop to ensure the function does not fail spuriously.  When a compare-and-exchange is in a loop, the weak version can yield better performance on some platforms.

#include <stdbool.h>
#include <stdatomic.h>

static atomic_bool cur;

void init_flag(void) {
  atomic_init(&cur, false);
}
 
void func(void) {
  init_flag();
 
  bool exp = atomic_load(&cur);
  bool des = !exp;
 
  while (!atomic_compare_exchange_weak(&cur, &exp, des)) {
    /* do nothing */
  }
}

Compliant Solution (atomic_compare_exchange_strong())

When a weak compare-and-exchange would require a loop and a strong one would not, the strong one is preferable.

#include <stdbool.h>
#include <stdatomic.h>

static atomic_bool cur;

void init_flag(void) {
  atomic_init(&cur, false);
}
 
void func(void) {
  init_flag();
 
  bool exp = atomic_load(&cur);
  bool des = !exp;
 
  if (!atomic_compare_exchange_strong(&cur, &exp, des) {
    /* Handle error */
  }
}

 

Risk Assessment

Failing to wrap the atomic_compare_exchange_weak() and atomic_compare_exchange_weak_explicit() functions in a loop can result in incorrect values and control flow.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON44-C

Low

Unlikely

Medium

P2

L3

Related Guidelines

Bibliography

[ISO/IEC 9899:2011]Subclause 7.17.7.4, "The atomic_compare_exchange Generic Functions"

[Lea 2000]

1.3.2, "Liveness"
3.2.2, "Monitor Mechanics"

 


  • No labels