27

有人可以解释如何atomicModifyIORef工作吗?尤其:

(1)它是等待锁定,还是乐观地尝试并在存在争用时重试(如TVar)。
(2) 为什么 的签名与atomicModifyIORef的签名不同modifyIORef?特别是,这个额外的变量是什么b

编辑:我想我已经找到了(2)的答案,因为这b是一个要提取的值(如果不需要,这可以是空的)。在单线程程序中,知道该值是微不足道的,但在多线程程序中,人们可能想知道在应用函数时之前的值是什么。我认为这就是为什么modifyIORef没有这个额外的返回值(因为这种返回值的用法modifyIORef可能无论如何都应该使用atomicModifyIORef。我仍然对(1)的答案感兴趣。

4

2 回答 2

34

它是等待锁定,还是乐观地尝试并在存在争用时重试(如 TVar)。

atomicModifyIORef 在您所在的底层硬件架构上使用锁定指令,以原子方式交换指向已分配 Haskell 对象的指针。

在 x86 上,它使用 cas 指令,作为原始语言暴露给 via atomicModifyMutVar#,它在 Cmm 中作为运行时服务实现为:

stg_atomicModifyMutVarzh
{
...

 retry:
   x = StgMutVar_var(mv);
   StgThunk_payload(z,1) = x;
#ifdef THREADED_RTS
   (h) = foreign "C" cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var, x, y) [];
   if (h != x) { goto retry; }
#else
   StgMutVar_var(mv) = y;
#endif
...
}

也就是说,它将尝试进行交换,否则重试。

cas 作为原语的实现展示了我们如何深入到金属:

/*
 * Compare-and-swap.  Atomically does this:
 */
EXTERN_INLINE StgWord cas(StgVolatilePtr p, StgWord o, StgWord n);

/*
 * CMPXCHG - the single-word atomic compare-and-exchange instruction.  Used
 * in the STM implementation.
 */
EXTERN_INLINE StgWord
cas(StgVolatilePtr p, StgWord o, StgWord n)
{
#if i386_HOST_ARCH || x86_64_HOST_ARCH
    __asm__ __volatile__ (
      "lock\ncmpxchg %3,%1"
          :"=a"(o), "=m" (*(volatile unsigned int *)p)
          :"0" (o), "r" (n));
    return o;
#elif arm_HOST_ARCH && defined(arm_HOST_ARCH_PRE_ARMv6)
    StgWord r;
    arm_atomic_spin_lock();
    r  = *p;
    if (r == o) { *p = n; }
    arm_atomic_spin_unlock();
    return r;
#elif !defined(WITHSMP)
    StgWord result;
    result = *p;
    if (result == o) {
        *p = n;
    }
    return result;

所以你可以看到它能够在 Intel 中使用原子指令,在其他架构上将使用不同的机制。运行时将重试。

于 2012-04-11T12:41:29.450 回答
13

atomicModifyIORef接受一个r :: IORef a和一个函数f :: a -> (a, b)并执行以下操作:

它读取 的值r并应用于f该值,产生(a',b)。然后r用新值更新,a'b返回值。这种读写访问是原子完成的。

当然,这种原子性只有在所有访问r都通过atomicModifyIORef. 请注意,您可以通过查看来源 [1] 找到此信息。

[1] https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-IORef.html#v:atomicModifyIORef

于 2012-04-11T09:21:20.627 回答