C++11 アトミック ライブラリを使用して seqlock を作成したいと考えています。stackoverflow の seqlock に関するいくつかの質問を読みましたが、誰も助けてくれませんでした。私が使用するアルゴリズムは一般的で、どこにでもあります。それが私のコードです。
struct sequence_spinlock_t {
void write_lock() {
lock.lock();
flags.fetch_add(1, memory_order_acquire); //A
}
void write_unlock() {
flags.fetch_add(1, memory_order_release); //B
lock.unlock();
}
void read_enter(uintptr_t *flag) {
for (;;) {
uintptr_t f = flags.load(memory_order_acquire); //C
if ((f & 1) == 0) {
*flag = f;
break;
}
pause();
}
}
bool_ read_leave(uintptr_t flag) {
uintptr_t f = flags.load(memory_order_relaxed); //D
return f == flag;
}
spinlock_t lock;
atomic_uintptr_t flags;
};
//read thread
uintptr_t flag;
do {
lock.read_enter(&flag); (0)
//read something (1)
} while(!lock.read_leave(flag)) (2)
//write thread
lock.write_lock(); (3)
//write something (4)
lock.write_unlock(); (5)
B と C で memory_order タグを正しく使用していることを確認します。
AとDでは正しくないと思います。
保護されたデータの読み取りと書き込みを同時に行うことを考えてみてください。D のフラグの読み取り値が古すぎて、write_lock() によって書き込まれた最新の値を読み取っていないのではないかと心配しています。しかし、保護されたデータの最新の値を読み取ります。書き込みスレッドによって書き込まれます (x86 システムでは発生しない可能性がありますが、コードが x86 で実行されているとは想定していません)。読み取りスレッドが保護されたデータの読み取りを終了した後、フラグの読み取り値が古すぎるため、シーケンスが増加したことがわかりません。読み取りスレッドがループから抜け出し、バグが発生します。
(1)で保護されたデータの読み取り値は(4)で書き込まれ、(2)で読み取られたフラグの値は(3)で書き込まれません(前回書き込みロックを解除したときに書き込まれます)。バグがあると思う理由。
しかし、これを修正する方法が本当にわかりません。read_leavee() と write_locke() の間に「同期」関係を作ろうとしました (「read_leave() を write_locke() と同期させたい」)。しかし、ストアはありません。アクションは read_leave() で、失敗しました。
(ああ、C++ の標準仕様は難しすぎて理解できません。私が英語圏ではないことも一因です。)