このスレッドサニツァー警告のレースはどこですか?


8

以下のコードは、macOSでスレッドサニタイザーを使用して実行すると警告を生成します。レースの場所がわかりません。shared_ptrおよびweak_ptrの制御ブロックはスレッドセーフであり、からのプッシュとポップstd::queueはロックを保持したまま行われます。

#include <future>
#include <memory>
#include <queue>

class Foo {
public:
  Foo() {
    fut = std::async(std::launch::async, [this] {
      while (!shouldStop) {
        std::scoped_lock lock(mut);
        while (!requests.empty()) {
          std::weak_ptr<float> requestData = requests.front();
          requests.pop();
          (void)requestData;
        }
      }
    });
  }

  ~Foo() {
    shouldStop.store(true);
    fut.get();
  }

  void add(const std::weak_ptr<float> subscriber) {
    std::scoped_lock lock(mut);
    requests.push(subscriber);
  }

private:
  std::atomic<bool> shouldStop = false;
  std::future<void> fut;
  std::queue<std::weak_ptr<float>> requests;
  std::mutex mut;
};

int main() {
  Foo foo;

  int numIterations = 100000;

  while (--numIterations) {
    auto subscriber = std::make_shared<float>();

    foo.add(subscriber);
    subscriber.reset();
  }

  std::this_thread::sleep_for(std::chrono::seconds(1));
}

スタックトレースに関する警告:

WARNING: ThreadSanitizer: data race (pid=11176)
  Write of size 8 at 0x7b0800000368 by thread T1 (mutexes: write M16):
    #0 operator delete(void*) <null>:1062032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x4f225)
    #1 std::__1::__shared_ptr_emplace<float, std::__1::allocator<float> >::__on_zero_shared_weak() new:272 (minimal:x86_64+0x1000113de)
    #2 std::__1::weak_ptr<float>::~weak_ptr() memory:5148 (minimal:x86_64+0x100010762)
    #3 std::__1::weak_ptr<float>::~weak_ptr() memory:5146 (minimal:x86_64+0x100002448)
    #4 Foo::Foo()::'lambda'()::operator()() const minimal_race.cpp:15 (minimal:x86_64+0x10000576e)
    #5 void std::__1::__async_func<Foo::Foo()::'lambda'()>::__execute<>(std::__1::__tuple_indices<>) type_traits:4345 (minimal:x86_64+0x1000052f0)
    #6 std::__1::__async_func<Foo::Foo()::'lambda'()>::operator()() future:2323 (minimal:x86_64+0x100005268)
    #7 std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >::__execute() future:1040 (minimal:x86_64+0x100005119)
    #8 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<Foo::Foo()::'lambda'()> >*> >(void*) type_traits:4286 (minimal:x86_64+0x10000717c)

  Previous atomic write of size 8 at 0x7b0800000368 by main thread:
    #0 __tsan_atomic64_fetch_add <null>:1062032 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x24cdd)
    #1 std::__1::shared_ptr<float>::~shared_ptr() memory:3472 (minimal:x86_64+0x1000114d4)
    #2 std::__1::shared_ptr<float>::~shared_ptr() memory:4502 (minimal:x86_64+0x100002488)
    #3 main memory:4639 (minimal:x86_64+0x10000210b)

SUMMARY: ThreadSanitizer: data race new:272 in std::__1::__shared_ptr_emplace<float, std::__1::allocator<float> >::__on_zero_shared_weak()

編集:私はそれをコンパイルします:

clang++ -std=c++17 -g -fsanitize=thread -o test  minimal_race.cpp

clangバージョン:

$ clang++ --version
clang version 7.1.0 (tags/RELEASE_710/final)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm@7/bin

macOS 10.14.6を使用しています



2
いいえ、ここでは大丈夫です。OPのコードに問題があります。彼らはそれを改善しようとしているだけではありません。競合状態は非常に深刻な問題です
Tas

Linuxでgccとclangを使用してスレッド
サニタイザで

これは潜在的なデッドロックを引き起こしませんか?ではstd::launch::asynccppreference.comstd::asyncに従ってリクエストをスケジュールする方法を決定する必要があります。つまり、が構築されると、mutexがロックされます(trueになるまでロック解除されません)。場合は、その後と呼ばれ、それはそれがないことはありませんこれは、将来的には、ロックを解除するのを待つ、ミューテックスをロックしようとします。FoofutureshouldStopFoo::Add
xEric_xD

1
Linuxのclangでlibc ++を使用してより適切なトレースを取得できました。ここに投稿する方法がわからないので、このリンクで確認できます。代わりにでコンパイルしましたclang++ -std=c++17 -O0 -ggdb -fsanitize=thread -stdlib=libc++ -o x x.cpp
。libstdc

回答:


1

これは、libc ++とclangのweak_ptr / shared_ptr実装のバグかもしれません...

weak_ptrキューをshared_ptrキューに変更すると、古いclangバージョンでも問題が解決します。

変更は25行目です。

  void add(const std::shared_ptr<float> subscriber) {

33行目:

  std::queue<std::shared_ptr<float>> requests;

そして42行目のwhileを次のように書き換えます:

  while (--numIterations) {
    auto subscriber = std::make_shared<float>();

    foo.add(move(subscriber));
  }

再現できましたか?どのclangバージョンですか?
Marek R

これはオブジェクトの破壊を防ぐので、私にはこれは論理的に聞こえます。のデストラクタで競合状態が発生しているようですshared_ptr
JHBonarius

clang ++ -v clangバージョン9.0.0(tags / RELEASE_900 / final)ターゲット:x86_64-pc-linux-gnuスレッドモデル:posix InstalledDir:/ usr / lib / llvm / 9 / bin選択したGCCインストール:/ usr / lib / gcc /x86_64-pc-linux-gnu/9.2.0 Candidate multilib:。; @ m64 Candidate multilib:32; @ m32 Selected multilib:。; @ m64 @MarekR
xception

@MarekR質問へのコメントとビルドフラグへのリンクを含む自分のバックトレースを投稿しました
xception

1
@JHBonarius私がweak_ptr / shared_ptrインタラクションが異なるスレッドからそれらを使用してどのように動作するかを正しく理解している場合(ポイントされたデータではなく、ラッパークラスのみ)、少なくともOPがshared_ptrとweak_ptrでそれらを使用する方法(異なるオブジェクトであること) )
xception '18年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.