由于我的编译器仍然不支持c ++ 11和std :: atomic,我不得不通过ldrex-strex对手动实现它。
我的问题是:使用ldrex和strex'原子'读取 - 修改 - 写入int64_t的正确方法是什么?
这样的简单解决方案似乎不起作用(STREXW中的一个一直返回1):
volatile int64_t value;
int64_t temp;
do
{
int32_t low = __LDREXW( (uint32_t *)&value );
int32_t high = __LDREXW( ((uint32_t *)&value)+1 );
temp = (int64_t)low | ( (int64_t)high<<32);
temp++;
} while( __STREXW( temp, (uint32_t *)&value) | __STREXW( temp>>32, ((uint32_t *)&value)+1) );
我找不到关于指向手册中不同地址的几个连续LDREX或STREX指令的任何内容,但在我看来应该允许它。
否则,在某些情况下,多个线程将无法更改两个不同的原子变量。
答案 0 :(得分:5)
这将永远不会有效,因为你无法以这种方式嵌套。在实现方面,Cortex-M3本地专用监视器甚至不跟踪地址 - the exclusive reservation granule is the entire address space - 所以单独跟踪每个单词的假设已经无效。但是,您甚至不需要考虑任何实现细节,因为该架构已经明确排除背靠背strex
:
如果在没有插入LDREX的情况下执行了两条STREX指令,则第二个STREX返回状态值1.这意味着:
- 在给定的执行线程中,每个STREX都必须有一个与之关联的LDREX。
- 每个LDREX都没有必要拥有后续的STREX。
由于Cortex-M3(和ARMv7-M一般)没有ldrexd
类似ARMv7-A,因此您必须使用单独的锁来控制对变量的所有访问,或者只是禁用read-modify-write周围的中断。如果可能的话,首先重新设计不需要原子64位类型的东西真的会更好,因为你仍然只能在同一个核心上实现相对于其他线程的原子性 - 你根本无法制作从外部代理(如DMA控制器)的角度来看,任何64位操作原子。
答案 1 :(得分:3)
我只是看看gcc是如何做到的,并使用相同的指令序列。
gcc 4.8.2 声明实施std::atomic<int64_t>
,is_lock_free()
返回true,即使使用-mcpu=cortex-m3
也是如此。 不幸的是,它并没有真正起作用。它使代码无法链接或不起作用,因为它没有实现它试图使用的辅助函数。 (感谢@Notlikethat试用它。)
Here's the test code I tried。如果该链接已死,请查看此答案的旧版本。我将这个答案留下来,以防这个想法对gcc 制作有用代码的相关案例中的任何人都有用。