ここで説明する状況は、iPad 4(ARMv7s)で発生しており、posixlibsを使用してロック/ロック解除をミューテックスします。ただし、他のARMv7デバイスでも同様のことが見られます(以下を参照)。したがって、どのソリューションでも、ARMv7のミューテックスとメモリフェンスの動作をより一般的に調べる必要があると思います。
シナリオの擬似コード:
スレッド1–データの生成:
void ProduceFunction() {
MutexLock();
int TempProducerIndex = mSharedProducerIndex; // Take a copy of the int member variable for Producers Index
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
MutexUnlock();
}
スレッド2–データの消費:
void ConsumingFunction () {
while (mConsumerIndex != mSharedProducerIndex) {
doWorkOnData (mSharedArray[mConsumerIndex++]);
}
}
以前(iPad 2で問題が発生したとき)、私はmSharedProducerIndex = TempProducerIndex
それがアトミックに実行されていないと信じていたので、AtomicCompareAndSwap
を割り当てるためにを使用するように変更しましたmSharedProducerIndex
。これはこの時点まではうまくいきましたが、私が間違っていたことが判明し、バグが戻ってきました。「修正」はタイミングを変えただけだと思います。
私は今、実際の問題はミューテックスロック内での書き込みのアウトオブオーダー実行であるという結論に達しました。つまり、コンパイラまたはハードウェアのいずれかが並べ替えを決定した場合です。
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
... に:
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
...そして、コンシューマーがプロデューサーをインターリーブした場合、コンシューマーがデータを読み取ろうとしたときに、データはまだ書き込まれていませんでした。
したがって、メモリバリアについて読んだ後、信号を外部の消費者に移動してみようと思いましmutex_unlock
た。ロックを解除すると、次の場所に確実mSharedArray
に書き込まれるメモリバリア/フェンスが生成されると信じていました。
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
MutexUnlock();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
mutex_unlock
しかし、これはまだ失敗し、aが確実に書き込みフェンスとして機能するかどうか疑問に思いますか?
また、コンパイラがコードをsに移動できる(ただし、sから移動できない)ことを示唆するHPの記事も読みましたcrit_sec
。したがって、上記の変更後でも、書き込みはmSharedProducerIndex
バリアの前にある可能性があります。この理論にマイレージはありますか?
明示的なフェンスを追加することで、問題は解消されます。
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index
OSMemoryBarrier();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
したがって、私は問題を理解しており、フェンスが必要であると思いますが、ロック解除の動作と、それがバリアを実行していないように見える理由についての洞察は非常に役立ちます。
編集:
コンシューマースレッドにミューテックスがないことについて:私はint mSharedProducerIndex
単一の命令であるという書き込みに依存しているため、コンシューマーが新しい値または古い値のいずれかを読み取ることを望んでいます。どちらも有効な状態であり、それmSharedArray
が順番に(つまり、書き込む前にmSharedProducerIndex
)書き込まれる場合、これは問題ありませんが、これまでの説明から、これについては返信できません。
mSharedProducerIndex
同じロジックにより、書き込みがバリア内に移動する可能性があり、したがって誤って並べ替えられる可能性があるため、現在のバリアソリューションにも欠陥があるように見えます。
読み取りバリアとして機能するために、コンシューマーにミューテックスを追加することをお勧めしますか、それともPPCpragma
のように、プロデューサーでアウトオブオーダー実行を無効にするための命令がありますか?EIEIO