众所周知arm是weakly ordered CPU. 参考1给出了详细的例子。
也就是说在
CPU0: X = 1; Y = 1;
CPU1: if (Y == 1) assert(x == 1);
由于X = 1; 可能发生在Y = 1; 后面。所以CPU1观察到Y = 1时,X可能还不是1,所以断言会失败。
写一个多线程程序验证:
#include <pthread.h>
#include <stdio.h>
#define assert(cond) if (!(cond)) printf("assert: %s failed\n", #cond)
volatile unsigned long x, y;
void *f1(void *)
{
while (1) {
if (y == 0) {
x = 1;
y = 1;
}
}
}
void *f2(void *)
{
while (1) {
if (y == 1) {
assert(x == 1);
x = 0;
y = 0;
}
}
}
void main()
{
pthread_t a, b;
pthread_create(&a, NULL, f1, NULL);
pthread_create(&b, NULL, f2, NULL);
pthread_join(a, NULL);
pthread_join(b, NULL);
}在两台arm64上面都不能验证。
同时仿照参考1中,写一个例子也不能验证:
#include <pthread.h>
#include <stdio.h>
#define assert(cond) if (!(cond)) printf("assert: %s failed\n", #cond)
volatile unsigned long lock;
volatile unsigned long count;
void *f(void *)
{
int i = 0;
while (i < 1000000) {
unsigned long zero = 0;
if (__atomic_compare_exchange_n(&lock, &zero, 1, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
count++;
__atomic_store_n(&lock, 0, __ATOMIC_RELAXED);
i++;
}
}
}
void main()
{
pthread_t a, b;
pthread_create(&a, NULL, f, NULL);
pthread_create(&b, NULL, f, NULL);
pthread_join(a, NULL);
pthread_join(b, NULL);
printf("count: %lu\n", count);
}这里relaxed模式,count++和__atomic_store_n(&lock, 0, __ATOMIC_RELAXED)乱序,就会导致保护失效。
最后向社区求助,如参考2,原来是两个内存访问在同一个cache line,不会乱序,简单修改一下,使x,y间隔64个字节。
volatile unsigned long x __attribute__((aligned(64))); volatile unsigned long y __attribute__((aligned(64)));
在ipq5018上面,打印出来了:
/ # ./a2.out
assert: x == 1 failed
assert: x == 1 failed
assert: x == 1 failed
assert: x == 1 failed
assert: x == 1 failed
^C
仿照参考1的例子,修改为:
volatile unsigned long lock __attribute__((aligned(64))); volatile unsigned long count __attribute__((aligned(64)));
/ # ./a.out
count: 1999998
/ # ./a.out
count: 1999998
/ # ./a.out
count: 1999999
/ # ./a.out
count: 1999997
乱序的概率为200万分之3。
总结:
在arm上面,不在同一个cacheline的内存访问更容易乱序。写例子的时候,尽量不在同一个cacheline.
参考
https://preshing.com/20121019/this-is-why-they-call-it-a-weakly-ordered-cpu/
https://stackoverflow.com/questions/79956349/arm64-weak-memory-model-why-this-assertx-1-not-failing