背景:
这也是一个真实客户案例,但我不能透露代码及问题细节,只讲思想。
问题:
客户说他们现场经常打印一句warning,我们对应源码查看了代码。伪代码如下:
void pin()
{while (!flag){ sleep(1);}if (hold > 0) {printf("warning: holder already set\n");exit(-1);}
}void unpin()
{hold = -1; //没人持有flag = 0; //无锁
}
pin 和unpin分数不同的进程,正如函数名所暗示分别用来上自旋锁和放自旋锁。这部分代码是不知名的前辈写的,开始只有flag的判断没有hold, 我猜测后来为了好查看自选锁到底被谁持有了才加了关于hold的代码,但是就是这暗藏BUG。
这个BUG隐藏的比较深
看到代码后我们过了好久才意识到可能有指令重排,而且是经过查看汇编代码才确认的。
主要是hold与flag的赋值的顺序在unpin中被交换了:本来我们想先hold=-1, 再flag=0,但是实际编译器却给优化成了flag=0, then hold=-1, 从而导致pin中报warning。
汇编代码大概是(注意:没与上面代码匹配)(两种办法获得汇编代码
1. gdb中disass unpin
2. objdump -d ./a.out |grep unpin)