秩序化されたノンレントラントの呼び出しに対する簡単なアサート?
-
28-10-2019 - |
質問
2つの機能があります。
void prepare()およびvoid finish()は、順次と呼ばれます。
prepare();
<do something>;
finish();
...
prepare();
<do something>;
finish();
私は、それらが実際にこのように呼ばれていること、そしてそれらがアプリケーションで同時に秩序外であると呼ばれていないことを単純にテストするために簡単な主張をしたいと思います。
このアプリケーションは、シングルスレッドアプリケーションです。これは、これらの機能が順序で呼ばれ、何らかの理由で同時に呼ばれていないことを確認するための単純な開発/テスト正気チェックです。さらに、パフォーマンスが重要であるため、これらのアサーション/正気のチェックは、生産コードから省略する必要があります!
このような単純なassert()が最もうまくいくでしょうか?
int test = 0;
void prepare() {
assert(++test == 1);
.
.
.
}
void finish() {
assert(--test == 0);
.
.
.
}
解決
ネストを許可する必要がない限り、コードは問題ありません prepare
と finish
電話。
ネスティングが許可されていない場合は、 bool
の代わりに int
:
bool locked = false;;
void prepare() {
assert( ! locked );
locked = true;
...
}
void finish() {
assert( locked );
locked = false;
...
}
他のヒント
あなたは変えたいかもしれません
int test = 0;
に
#ifndef NDEBUG
int test = 0;
#endif
「このテストに関連するコードは、生産から省略する必要がある」という要件を満たすために。
おそらく欲しい:
int test = 0;
void prepare() {
// enter critical section
assert(test++ == 0);
.
.
.
// leave critical section
}
void finish() {
// enter critical section
assert(--test == 0);
.
.
.
// leave critical section
}
ここには人種の状態があります:の2つの同時インスタンス prepare
の価値を取るかもしれません test
同時に、両方ともレジスタでそれを増やして両方を取得します 1
, 、次に比較して取得します true
.
それを作る volatile
は 助けにはならない. 。代わりに、ミューテックスを装着する必要があります test
, 、 そのようです:
boost::mutex mtx;
int test = 0;
void prepare()
{
boost::mutex::scoped_try_lock lock(&mtx);
assert(lock.owns_lock());
assert(test++ == 0);
// ...
}
void finish()
{
boost::mutex::scoped_try_lock lock(&mtx);
assert(lock.owns_lock());
assert(--test == 0);
}
あなたが置くなら <do something>;
に class
チェックの必要性をまったく減らすことができます。
コンストラクターに電話をかけるだけです prepare
そして、デストラクタコール finish
. 。その後、それらが適切に呼ばれることが自動的に強制されます。
並行性とネスティングの問題がまだ適用されることに注意してください:ネスティングを防ぐ場合は、それを追跡するには、何らかのグローバルな状態(静的クラスメンバー?)が必要であり、それが複数のスレッドアクセスで使用されている場合は、それを追跡する必要があります。カウンターはMutex保護される必要があります。
また、プライベートにすることもできることに注意してください operator new/delete
誰かがヒープでそれを作成し、それを破壊しないのを防ぐため。
C ++を使用しているので、Raiiを使用してみませんか?再入国の使用を確認する必要がありますが、Raiiは物事を大幅に簡素化します。と組み合わせ Larsmansのミューテックス と NdebugでのRaedwaldの排除:
struct Frobber {
Frobber() {
assert(mtx.try_lock());
#ifndef NDEBUG
try { // in case prepare throws
#endif
prepare();
#ifndef NDEBUG
}
catch (...) {
mtx.unlock();
throw;
}
#endif
}
void something();
// And the other actions that can be performed between preparation and finishing.
~Frobber() {
finish();
#ifndef NDEBUG
mtx.unlock();
#endif
}
private:
#ifndef NDEBUG
static boost::mutex mtx;
#endif
Frobber(Frobber const&); // not defined; 0x: = delete
Frobber& operator=(Frobber const&); // not defined; 0x: = delete
};
#ifndef NDEBUG
boost::mutex Frobber::mtx;
#endif
void example() {
Frobber blah; // instead of prepare()
blah.something();
// implicit finish()
}
例の中で、あなたは単に できません 例外がスローされていても、最初に準備せずに何かをしてください。
ndebugについてのサイドノート:この方法で使用する場合は、常に定義されているか、常に定義されていないことを確認してください 全て 翻訳ユニットは、アサートにどのように使用されるかとは対照的に(さまざまなポイントで定義および未定義にすることができます)。