yohhoyの日記

技術的メモをしていきたい日記

atomic変数間のatomicなコピー

プログラミング言語C/C++が提供するatomic変数*1とそのコピー操作に関するメモ。

2個のatomic変数間における “atomicなコピー操作” は提供されない。通常は “atomic変数からの読込(load)”+“atomic変数への格納(store)” で代用すればよい。ただし、下記コードのような2つのコピー操作が並行実行された場合、load/store操作のインターリーブが起こりうることに留意。*2

// C++11
#include <atomic>
using namespace std;
// C11
#include <stdatomic.h>

atomic_int x = ATOMIC_VAR_INIT(0);  // x = 0
atomic_int y = ATOMIC_VAR_INIT(1);  // y = 1

void thread1()
{
  atomic_store( &x, atomic_load(&y) );  // x = y;
}
void thread2()
{
  atomic_store( &y, atomic_load(&x) );  // y = x;
}
// 並行実行の結果として x==1 && y==0 となる可能性あり

2022-07-25追記:ATOMIC_VAR_INITマクロはC17で非推奨(deprecated)、C2x(C23)で削除される*3C++20で非推奨(deprecated)*4C++2a(C++23)またはC++2b(C++26)で削除される公算が大きい*5

真に “atomicなコピー操作” が必要ならば、mutex等を用いた排他制御を行うこと。ただし、この場合atomic変数とする必然性はない。

// C++11
#include <mutex>
int x = 0, y = 1;
std::mutex m;

void thread1()
{
  std::lock_guard<decltype(m)> lk(m);
  x = y;
}
void thread2()
{
  std::lock_guard<decltype(m)> lk(m);
  y = x;
}
// 実行結果は x==y==0 または x==y==1 のいずれか

C++11

#include <atomic>

std::atomic<int> x;
std::atomic<int> y = ATOMIC_VAR_INIT(42);

x = y;  // NG: ill-formed
x.store(y.load());  // OK

std::atomic<T>クラステンプレートでは、コピーコンストラクタ/コピー代入演算子がdelete指定されている。JTC1/SC22/WG21 N3337 29.5/p3より引用。

3 Specializations and instantiations of the atomic template shall have a deleted copy constructor, a deleted copy assignment operator, and a constexpr value constructor.

C11

#include <stdatomic.h>

_Atomic int x;
_Atomic int y = ATOMIC_VAR_INIT(42);

x = y;  // OK: ただし下記コードと等価
// atomic_store(&x, atomic_load(&y));

代入式の右辺ではlvalue conversionにより、atomic型から非atomic型への変換(値のload)が行われる。JTC1/SC22/WG14 N1570 6.2.6.1/p9, 6.3.2.1/p2より一部引用。

9 Loads and stores of objects with atomic types are done with memory_order_seq_cst semantics.

2 (snip); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. (snip)

関連URL

*1:C11:_Atomic T または _Atomic(T) または atomic_TC++11:std::atomic<T> または std::atomic_T

*2:非 memory_order_seq_cst による弱いメモリ順序に起因する事象とは異なり、逐次一貫性(sequential consistency)実行の下でも生じる事象。

*3:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2886.htm

*4:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0883r2.pdf

*5:https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2139r2.html#3.24