dbgroup-nagoya-u / pmem-atomic

A utility library for atomic operations in persistent memory.
Apache License 2.0
0 stars 0 forks source link

記述子を配列で保持 #4

Closed manabinohibi closed 2 years ago

manabinohibi commented 2 years ago

記述子のプールを定義したクラスをなんとなくで書いてみました. が, 現状[build] /usr/lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_algobase.h:760:11: error: object of type 'std::pair<std::atomic<bool>, dbgroup::atomic::pmwcas::PMwCASDescriptor>' cannot be assigned because its copy assignment operator is implicitly deleted [clang-diagnostic-error] といったエラーが出ます. 調べてもよくわからずお手上げ状態なので,ご教授いただければ幸いです.

なお,pmwcas_descriptor.hppの場所はとりあえずそのままにしてます.ややこしくなりそうなので.

baycedar commented 2 years ago

すっかり忘れてましたが,std::atomicはコピー・ムーブコンストラクトが不可(=deleteで定義されている)でした^1.一方で,std::arrayを使うときは中に入れる型Tがコピーコンストラクト可能であることが要求されます(fill関数などの実行にインスタンスのコピーが必要なため).そのエラーメッセージはその点を警告している文章で,解決策としてはCの配列を使ってfill関数の代わりにforループで初期化すればたぶん通ると思います.

manabinohibi commented 2 years ago

どうもありがとうございます!無事にビルドできました.

manabinohibi commented 2 years ago

とりあえず空いている記述子を取得するところまでかけた(つもり)なのですが,この場合どのようなテストをすべきなのでしょうか…?

baycedar commented 2 years ago

Descriptor poolの機能としては,

  1. 新規スレッドからdescriptorの取得要求が来たら新しいものを割り当て,返す.
  2. 既知のスレッドから取得要求が来たら割り当て済みのものをそのまま返す.
    • Poolが埋まっている場合は空くまで待機?
  3. Descriptor取得済みのスレッドが終了したら該当するdescriptorを未割当状態にする.

という感じなのでテストとしては,

  1. 単一のスレッド(i.e., メインプロセス)で複数回descriptorを取得し,同じdescriptorが得られているか確認.
  2. Poolの容量分のスレッドを立てて,各スレッドで一意なdescriptorが取得できるか確認.
  3. Poolの容量分のスレッドでdescriptorを取得した(i.e., poolに空きなしの)状態でメインプロセスでdescriptorの取得を要求し,何も返ってこない(i.e., descriptorの重複割当がない)ことを確認.その後,全てのスレッドを一度終了させ,再度poolの容量分のスレッドを立ち上げてdescriptorが取得できることを確認.

の3つくらいかなと思います.3のテストが割りと難しいのと,3の機能(descriptorの未割当状態への遷移)は別のクラスを用意してあげないと実現できない気がちょっとしています.

baycedar commented 2 years ago

たぶん,以下のようなクラスElementHolderを用意して,これをthread_localで保持してあげる必要がある気がします.このクラスはデストラクタでis_reservedfalseにするので,スレッド終了時に自動的に保持していたPoolElementの所有フラグを消せます.

struct PoolElement {
 public:
  /// a flag for indicating this element is reserved.
  std::atomic_bool is_reserved{false};

  /// a target instance.
  PMwCASDescriptor desc{};
}

class ElementHolder
{
 public:
  /*####################################################################################
   * Public constructors and assignment operators
   *##################################################################################*/

  explicit ElementHolder(PoolElement *element) : element_{element} {}

  ElementHolder(const ElementHolder &) = delete;
  ElementHolder(ElementHolder &&) = delete;
  auto operator=(const ElementHolder &) -> ElementHolder & = delete;
  auto operator=(ElementHolder &&) -> ElementHolder & = delete;

  /*####################################################################################
   * Public destructors
   *##################################################################################*/

  ~ElementHolder
  {
    element_->is_reserved.store(false, std::memory_order_relaxed);
  }

  /*####################################################################################
   * Public getters
   *##################################################################################*/

  constexpr auto
  GetHoldDescriptor() const  //
    -> PMwCASDescriptor *
  {
    return &(element_->desc);
  }

 private:
  /*####################################################################################
   * Internal member variables
   *##################################################################################*/

  /// an address of an actual element.
  PoolElement *element_{};
}
manabinohibi commented 2 years ago

たいへん詳しくありがとうございます! とりあえず簡単そうな1からやってみたいと思います.

manabinohibi commented 2 years ago

@baycedar とりあえず以下の2つのテストを書いてみたので,少し見ていただきたいです.

  1. 1つのスレッドで2つの記述子ポインタを取得,同じものかどうか
  2. プールサイズ分のスレッドで記述子ポインタを取得,全て異なるかどうか

テストこそ通りましたが,正しいテストがかけているのか,きちんとマルチスレッドで動いているのか等怪しさたくさんです.

よろしくお願いいたします.

manabinohibi commented 2 years ago

と,思ったらPRでのテストは失敗してました. 明日確認します.

manabinohibi commented 2 years ago

修正しました!

baycedar commented 2 years ago

一旦レビューを外しておくので,テストを書いてチェックして欲しくなったら再度割当をお願いします.

manabinohibi commented 2 years ago

@baycedar

5 での内容を反映したので一度ざっくり見てほしいです.テストとしては

  1. 1つのスレッドで2つの記述子ポインタを取得,同じものかどうか
  2. プールサイズ分のスレッドで記述子ポインタを取得,全て異なるかどうか

の2つです. 結局std::futureらを使っていないので,なにか間違っている気がします.

あと少し困った(解決策についてなにも検討していない)のは,

Poolの容量分のスレッドでdescriptorを取得した(i.e., poolに空きなしの)状態でメインプロセスでdescriptorの取得を要求し,何も返ってこない(i.e., descriptorの重複割当がない)ことを確認.

しようと思ったときに,今の実装だと空きができるまでループするので,無限ループになってしまう気がします. 実際使う分には,いつかは空きができると思いますが…

manabinohibi commented 2 years ago

ありがとうございます! 全体的にリファクタリングした後3つ目のテストを書いてみます!

manabinohibi commented 2 years ago

一度クローズします…