[英]Haskell: How does 'atomicModifyIORef' work?
Can someone explain how atomicModifyIORef
works?有人可以解释
atomicModifyIORef
是如何工作的吗? In particular:特别是:
(1) Does it wait for a lock, or optimistically try and retry if there's contention (like TVar
). (1) 它是等待锁定,还是乐观地尝试并在存在争用时重试(如
TVar
)。
(2) Why is the signature of atomicModifyIORef
different to the signature of modifyIORef
? (2) 为什么 atomicModifyIORef 的签名与
atomicModifyIORef
的签名modifyIORef
? In particular, what is this extra variable b
?特别是,这个额外的变量
b
是什么?
Edit: I think I've figured out the answer to (2), in that
b
is a value to be extracted (this can be empty if not needed).编辑:我想我已经找到了 (2) 的答案,因为
b
是要提取的值(如果不需要,它可以为空)。 In a single threaded program, knowing the value is trivial, but in a multithreaded program, one may want to know what the previous value was at the time of the function being applied.在单线程程序中,知道该值是微不足道的,但在多线程程序中,人们可能想知道在应用 function 时的先前值是什么。 I assume this is why
modifyIORef
doesn't have this extra return value (as such usages of modifyIORef
with this return value probably should use atomicModifyIORef
anyway. I'm still interested in the answer to (1) though.我认为这就是为什么
modifyIORef
没有这个额外的返回值(因为带有这个返回值的modifyIORef
的用法可能无论如何都应该使用atomicModifyIORef
。不过我仍然对(1)的答案感兴趣。
Does it wait for a lock, or optimistically try and retry if there's contention (like TVar).
它是等待锁定,还是乐观地尝试并在存在争用时重试(如 TVar)。
atomicModifyIORef uses an locking instruction on the underlying hardware architecture you're on, to swap the pointer to an allocated Haskell object in an atomic fashion. atomicModifyIORef 在您所在的底层硬件架构上使用锁定指令,以原子方式将指针交换到分配的 Haskell object。
On x86 it uses the cas intruction, exposed as a primitive to the language via atomicModifyMutVar#
, which is implemented as a runtime service in Cmm as:在 x86 上,它使用 cas 指令,通过
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
...
}
That is, it will try to do the swap, and retry otherwise.也就是说,它将尝试进行交换,否则重试。
The implementation of cas as a primitive shows how we get down to the metal: 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;
So you can see that it is able to use an atomic instruction in Intel, on other architectures different mechanisms will be used.所以你可以看到它能够在 Intel 中使用原子指令,在其他架构上将使用不同的机制。 The runtime will retry.
运行时将重试。
atomicModifyIORef
takes a r:: IORef a
and a function f:: a -> (a, b)
and does the following: atomicModifyIORef
采用r:: IORef a
和 function f:: a -> (a, b)
并执行以下操作:
It reads the value of r
and applies f
to this value, yielding (a',b)
.它读取
r
的值并将f
应用于该值,产生(a',b)
。 Then the r
is updated with the new value a'
while b
is the return value.然后用新值
a'
更新r
,而b
是返回值。 This read and write access is done atomically.这种读写访问是自动完成的。
Of course this atomicity only works if all accesses to r
are done via atomicModifyIORef
.当然,只有通过
atomicModifyIORef
完成对r
的所有访问时,这种原子性才有效。 Note that you can find this information by looking at the source [1].请注意,您可以通过查看来源 [1] 找到此信息。
[1] https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-IORef.html#v:atomicModifyIORef [1] https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-IORef.html#v:atomicModifyIORef
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.