在Android上比较和交换(ARM)

时间:2015-01-22 18:20:09

标签: android c assembly arm atomic

下面的代码是android上的比较和交换的ARM实现:

__ATOMIC_INLINE__ int __bionic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t* ptr) {
  int32_t prev, status;
  do {
    __asm__ __volatile__ (
          "ldrex %0, [%3]\n"
          "mov %1, #0\n"
          "teq %0, %4\n"
#ifdef __thumb2__
          "it eq\n"
#endif
          "strexeq %1, %5, [%3]"
          : "=&r" (prev), "=&r" (status), "+m"(*ptr)
          : "r" (ptr), "Ir" (old_value), "r" (new_value)
          : "cc");
  } while (__builtin_expect(status != 0, 0));
  return prev != old_value;
}

然后,即使条件不相等,strexeq也会清除ldrex中的监视器集,如果不相同,那么这样做是否安全?

为什么我们为thumb2需要额外的it eq呢?

1 个答案:

答案 0 :(得分:3)

  

即使条件不相等,strexeq是否清除ldrex中的监视器集?

没有。它也不需要 - 这是cmpxchg的“cmp”部分 - 如果加载的值不是预期值,那么teq给出ne条件,没有任何反应,我们就会失败循环由于mov %1, #0,返回,并且每个人都忘记了整个事情。

如果加载的值 是正确的那么我们会尝试条件strex进行交换。

所有ldrex确实设置了一个标志(独占监视器),说“自ldrex以来没有人触及过这个记忆区域”。如果有人写入该区域,则清除该标志。当且仅当它仍然设置了标志时,strex才会成功。如果它发现标志已清除,则表示加载的值可能已在内存中更改,这违反了操作的原子性,因此存储失败并且不进行更新。在这种情况下,我们必须回到开头并从头开始重试 - 最终,我们将不间断地完成整个序列,此时它似乎是原子更新。

在任何一种情况下都无需担心独占显示器状态;根据定义,任何后来的独占代码都以ldrex开头,这将在那时恰当地初始化监视器。

  

为什么我们为thumb2需要额外的it eq呢?

因为Thumb没有条件执行(分支除外),因此在指令编码中没有用于嵌入条件代码的位。 Thumb-2引入the it instruction作为通过全局ITSTATE对特定条件(或其相反)进行预测的最多4个后续指令的方法。虽然某些汇编程序足够聪明,可以在为Thumb-2组装ARM代码时自动生成适当的it块,但这并不是您在移植代码中必须依赖的东西。

一个表现良好的汇编程序在为ARM组装时应该忽略it(但是如果它与下面的指令中的条件不匹配仍然是错误的),但它可能是为了愚蠢的利益而预处理的通过计算换行符来猜测内联asm块长度的编译器。