非常に単純なメモリプールで作業しており、解決できなかった非常に興味深いバグを発見しました。ポインタの逆参照時の競合条件
アルゴリズムの考え方は次のとおりです。「利用可能な」メモリチャンクのスタックがあるため、各チャンクは次の利用可能なチャンクへのポインタを持ちます。二次的なデータ構造を避けるために、この同じメモリチャンクを使用してポインタを格納することにしました。したがって、次の利用可能なチャンクは、このチャンクを逆参照することによって得られます。void * nextChunk = *((void **)チャンク)
このコードは元々C++アトミックで実装されていますが、アトミック組み込み関数:私たちは、この問題をデバッグするために実行してきた試験では
void *_topChunk;
void *getChunk()
{
void *chunk;
// Try to reserve a chunk (for these tests, it has been forced that _topChunk can never be null)
do {
chunk = _topChunk;
} while(!__sync_bool_compare_and_swap(&_topChunk, chunk, *((void **)chunk)));
return chunk;
}
void returnChunk(void *chunk)
{
do {
*((void **)chunk) = _topChunk;
} while (!__sync_bool_compare_and_swap(&_topChunk, *((void **)chunk), chunk));
}
が、我々はこれを行う複数のスレッド生成:実行中のある時点で
while (1) {
void *ptr = getChunk();
*((void **)ptr) = (void *)~0ULL;
returnChunk(ptr);
}
を、GetChunkメソッド()セグメンテーションフォルト0xfff ...ポインタを逆参照しようとしているためです。しかし、returnChunk()、*((void **)chunk)に書かれているものは決して0xfffであってはなりません。スタックからの有効なポインタでなければなりません。それはなぜ機能しないのですか?
また、逆参照の代わりに中間のvoid *を使用しようとしましたが、結果はまったく同じです。
あなたは私たちの許可を求めずにやりたいの:) –
*二次データ構造を有する避けるために、我々は、使用することを決めましたポインタを格納するこのメモリチャンク。したがって、次の利用可能なチャンクは、このチャンクを参照解除することによって取得されます。void * nextChunk = *((void **)チャンク)*これは根本的に壊れているようです。これにより、2つ以上のスレッドが同じ古い値を同時に見てから、同じ新しい値を同時に書き込むことを防ぐ方法はありますか? –
@AndrewHenle getChunk()関数では、compare&swapは、取得したチャンクが同時にアクセスするスレッドごとに異なることを保証します。 returnChunk()関数の場合、次のチャンクへのポインタは "公開される"前に更新されます(スタックにプッシュされます)。少なくとも、投稿されたコードでこれが起こっているはずです。 – markmb