ロード・リンク/ストア排他的パラダイムの背後にある考え方は、もしあれば店が介在しないメモリ操作で、負荷後すぐに追従し、他に何も場所に触れていない場合は、ストアがであるということです可能であればが成功する可能性がありますが、何か他の場所にアクセスした場合は、が失敗します。明白な理由がなくても店舗が失敗することはないという保証はありません。ロードとストアの間の時間は、しかし、最小限に抑え、そして何のメモリがそれらの間にアクセスしないがある場合は、のようなループ:
do
{
new_value = __LDREXW(dest) + 1;
} while (__STREXW(new_value, dest));
は、一般的に数の試行中に成功するために頼ることができます。新しい値が計算されている間、このコードは何かが変更された場合* DESTをそのメインループを再実行する必要があります
do
{
old_value = *dest;
new_value = complicated_function(old_value);
} while (CompareAndStore(dest, new_value, old_value) != 0);
... Assuming CompareAndStore is something like:
uint32_t CompareAndStore(uint32_t *dest, uint32_t new_value, uint_32 old_value)
{
do
{
if (__LDREXW(dest) != old_value) return 1; // Failure
} while(__STREXW(new_value, dest);
return 0;
}
:古い値に基づいて新しい値を計算すると、いくつかの重要な計算を必要に応じて、1はループを書き換える必要がありますが、唯一の小さなループが
補遺 [のみ__LDREXWと__STREXWの間に約2つの命令があるだろうことを考えると、できればあまりそうではありません] __STREXWは、他のいくつかの理由で失敗した場合に再実行する必要があります「古いものに基づいて新しい価値を計算する」という複雑な状況の例は、「値」ar効果的に複雑なデータ構造への参照コードは古い参照をフェッチし、古いものから新しいデータ構造を派生させ、次にその参照を更新することができる。このパターンは、「ベアメタル」プログラミングよりもガベージコレクションフレームワークではるかに頻繁に現れますが、ベアメタルをプログラミングするときでさえさまざまな方法があります。通常のmalloc/callocアロケータは、一般的にはスレッドセーフ/割り込みセーフではありませんが、固定サイズ構造のアロケータはしばしばあります。頻繁にしようと何でも複数のスレッド/割り込み/存在しない場合
#define FOO_POOL_SIZE_SHIFT 8
#define FOO_POOL_SIZE (1 << FOO_POOL_SIZE_SHIFT)
#define FOO_POOL_SIZE_MASK (FOO_POOL_SIZE-1)
void do_update(void)
{
// The foo_pool_alloc() method should return a slot number in the lower bits and
// some sort of counter value in the upper bits so that once some particular
// uint32_t value is returned, that same value will not be returned again unless
// there are at least (UINT_MAX)/(FOO_POOL_SIZE) intervening allocations (to avoid
// the possibility that while one task is performing its update, a second task
// changes the thing to a new one and releases the old one, and a third task gets
// given the newly-freed item and changes the thing to that, such that from the
// point of view of the first task, the thing never changed.)
uint32_t new_thing = foo_pool_alloc();
uint32_t old_thing;
do
{
// Capture old reference
old_thing = foo_current_thing;
// Compute new thing based on old one
update_thing(&foo_pool[new_thing & FOO_POOL_SIZE_MASK],
&foo_pool[old_thing & FOO_POOL_SIZE_MASK);
} while(CompareAndSwap(&foo_current_thing, new_thing, old_thing) != 0);
foo_pool_free(old_thing);
}
:1のデータ構造の一部2のべき乗の数の「プール」を持っている場合は1のようなものを使用することができ、(255と言います)同時に同じことを更新すると、この方法では更新を安全に実行できるはずです。同じ項目を更新しようとする可能性のあるものの中に優先順位の関係が存在する場合、最も優先順位の高いものが最初の試行で成功することが保証され、次に優先順位の高いものが優先されます。ロックを使用していた場合、更新を実行したい最も優先度の高いタスクは、優先度の低い更新が終了するまで待つ必要があります。 CompareAndSwapパラダイムを使用すると、最も優先度の高いタスクは下位のタスクの影響を受けません(しかし、下位のタスクは無駄な作業をする必要があります)。
私はまったく同じことをやってきましたが、重要なコンピューティングが新しい価値に必要な部分は、私をまだ困惑させます。コンテキストスイッチで排他的なモニターをクリアすることはできないが、重要なコンピューティングを再実行するには、明らかに理由がなく、通りが壊れているのを見ているので、cmxchgループを使用することは意味がある(PSRでマスクされたIRQ )あなたのポストに記載されています。 – sgupta
@ user1075375:addendum – supercat
これらの(__LDREXWおよび__STREXW)は、主流ARMターゲット(たとえばAArch64)およびコンパイラ(gcc、llvmなど)では一般的に使用できないCortex-Mシリーズマイクロコントローラレベルのプロセッサ用のKeilコンパイラでサポートされている組み込み関数です) 右? http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABDEEJC.html – ahcox