アラインされたポインターのロードとストアがターゲット プラットフォーム上で自然にアトミックであると仮定すると、次の違いは何ですか。
// Case 1: Dumb pointer, manual fence
int* ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr = new int(-4);
これ:
// Case 2: atomic var, automatic fence
std::atomic<int*> ptr;
// ...
ptr.store(new int(-4), std::memory_order_release);
この:
// Case 3: atomic var, manual fence
std::atomic<int*> ptr;
// ...
std::atomic_thread_fence(std::memory_order_release);
ptr.store(new int(-4), std::memory_order_relaxed);
それらはすべて同等であるという印象を受けましたが、Relacyは最初のケース (のみ) でデータ競合を検出します。
struct test_relacy_behaviour : public rl::test_suite<test_relacy_behaviour, 2>
{
rl::var<std::string*> ptr;
rl::var<int> data;
void before()
{
ptr($) = nullptr;
rl::atomic_thread_fence(rl::memory_order_seq_cst);
}
void thread(unsigned int id)
{
if (id == 0) {
std::string* p = new std::string("Hello");
data($) = 42;
rl::atomic_thread_fence(rl::memory_order_release);
ptr($) = p;
}
else {
std::string* p2 = ptr($); // <-- Test fails here after the first thread completely finishes executing (no contention)
rl::atomic_thread_fence(rl::memory_order_acquire);
RL_ASSERT(!p2 || *p2 == "Hello" && data($) == 42);
}
}
void after()
{
delete ptr($);
}
};
Relacy の作成者に連絡して、これが予期された動作であるかどうかを確認しました。彼は、私のテストケースには確かにデータ競合があると言います。しかし、私はそれを見つけるのに苦労しています。誰かが私にレースが何であるかを指摘できますか? 最も重要なことは、これら 3 つのケースの違いは何ですか?
更新: Relacy は、スレッド間でアクセスされる変数のアトミック性(またはその欠如) について単に不平を言っている可能性があることに気づきました...結局のところ、このコードをプラットフォームでのみ使用するつもりであることを認識していません。ここで、整列された整数/ポインター アクセスは当然アトミックです。
別の更新: Jeff Preshing が、明示的なフェンスと組み込みのフェンス (「フェンス」と「操作」)の違いを説明する優れたブログ投稿を書きました。ケース 2 と 3 は明らかに同等ではありません。(とにかく、特定の微妙な状況では。)