New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::shared_ptr<>仮実装中に問題発生 #18

Closed
yossi-tahara opened this Issue Jan 9, 2017 · 1 comment

Comments

Projects
None yet
1 participant
@yossi-tahara
Owner

yossi-tahara commented Jan 9, 2017

単純に回復すれば良いと思っていたのですが、複数回保存して回復した時、参照カウンタが回復できません。
boostのshared_ptrのシリアライズ・コードを確認したところ、この問題に対策されてました。
シリアライズしたshared_ptrをシリアライズする際、shared_ptrの追跡テーブルをstd::mapにて別途管理してました。firstはオブジェクトのアドレス、secondはshared_ptr自身です。
しかし、実装は意外に複雑です。

  1. 2番目以降の基底クラスへのポインタと派生クラス・オブジェクトのポインタはアドレスが一致しません。
    なので、shared_ptr追跡テーブルへは派生クラス・オブジェクトのアドレスを登録しています。

  2. 更に回復処理時、aliasingコンストラクタを使っています
    これは、元のshared_ptrと参照カウンタ、および、デリート処理(デリート先オブジェクトとデリータ)を共有しているがポイント先を自由に選択できるshared_ptrのコントラクタです。ポイント先の寿命が元の寿命以上の場合に使えます。

なぜ、aliasingコンストラクタを使っているのか非常に不思議だったのですが、この対策は近藤貴俊氏が行っていて丁寧に解説されていました。
簡単に説明すると、派生クラスへのポインタを管理しているshared_ptrと、それと同じインスンタスを2番目以降の基底クラスへのポインタで管理しているshart_ptrの2つを保存し、回復した時に不具合が出るのです。

shared_ptr追跡テーブルはshared_ptr<派生クラスの型>を使うことも可能な筈ですが、boostはstd::shared_ptr<void>型で管理しています。
そのため、std::mapに登録されているshared_ptrを単純に返却すると、基底クラスへのポインタに派生クラスのアドレスが設定され、ずれてしまいます。
この問題を回避するために、aliasingコンストラクタをうまいこと使っていました。(これを使わないと複雑なことになりそうです。)

この2点の対処を実装する予定です。

yossi-tahara added a commit that referenced this issue Jan 9, 2017

MinGW File too big問題のため、オブジェクト追跡テスト用のソース・ファイル分割
オブジェクト追跡の使い方記述中
  サンプル・ソース用にstd::shared_ptr<>仮実装中、参照カウンタが回復できない問題発生。
    boostを参考に対策予定。 Issue #18, Issue #19
  非侵入型手動が、完全自動型を基底クラスとする時、基底クラスのシリアライズ・コードが必要になることを見落としていた。
    ドライバを修正して対処。(parse.h)

----------------- MD5 Hash Values -----------------
TheolizerDriver  : 1338faca054a45cfafd528d1beb18443
TheolizerLibrary : 52a86abf82699c0d0bb9324e0937507f
Library's Header : 801ac370a3115ac4e0c5d140281a5388

----------------- MD5 Hash Values -----------------
TheolizerDriver  : 1338faca054a45cfafd528d1beb18443
TheolizerLibrary : 52a86abf82699c0d0bb9324e0937507f
Library's Header : 801ac370a3115ac4e0c5d140281a5388

yossi-tahara added a commit that referenced this issue Jan 9, 2017

派生クラスを保存した時、基底クラスへのポインタの保存/回復に成功することを確認するテスト・コード追加。 Issue #18
----------------- MD5 Hash Values -----------------
TheolizerDriver  : 1338faca054a45cfafd528d1beb18443
TheolizerLibrary : 52a86abf82699c0d0bb9324e0937507f
Library's Header : 801ac370a3115ac4e0c5d140281a5388

yossi-tahara added a commit that referenced this issue Jan 10, 2017

std::shared_ptr対応 Issue #18
  2番目以降の基底クラス・ポインタ経由での保存/回復にも対応した。

----------------- MD5 Hash Values -----------------
TheolizerDriver  : 45789af1e4767bbd512346e999c0ac13
TheolizerLibrary : 2c9f2c1003ffe4be86f1f7b66f92c3be
Library's Header : 94b1966d98f69916da5114ab54e2b309
@yossi-tahara

This comment has been minimized.

Owner

yossi-tahara commented Jan 10, 2017

上記内容を実装しました。

なお、aliasingコンストラクで生成されたstd::shared_ptr<>自体は原則としてboost同様シリアライズ非対応となります。

対応するためには、「参照カウンタ、および、デリート処理(デリート先オブジェクトとデリータ)」を共有するstd::shared_ptrを生成する必要があります。そのためにどれが共有するべきstd::shared_ptrか判定が必要です。デリート先オブジェクトのアドレスを取得できれば、これをキーにして共有するべきstd::shared_ptrを判定できると思いますが、それを取得できるI/Fが定義されていないようです。(デリータはとれますが、これはstd::shared_ptrを管理するキーとしては使えません。)
共有するstd::shared_ptr間で共通なキーを取得する術があれば対応できると思いますが、現時点では非対応とします。

なお、現在は、上記のキーとしてインスタンス先頭のポインタを使っています。(2番目以降の基底クラスからポイントしていても派生クラス・サーチして取得しています。)
ですので、std::shared_ptrの一般的な使い方に対しては対応できていると思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment