2012-04-11 4 views
25

Может кто-нибудь объяснить, как работает atomicModifyIORef? В частности:Haskell: Как работает «atomicModifyIORef»?

(1) Дождаться блокировки или оптимистично попытаться повторить попытку (например, TVar).
(2) Почему подпись atomicModifyIORef отличается от подписи modifyIORef? В частности, что это за переменная b?

Редактировать: Я думаю, что я выяснил ответ на (2), в котором b - это значение, которое нужно извлечь (это может быть пустым, если не нужно). В однопоточной программе знание значения тривиально, но в многопоточной программе, возможно, захочется узнать, каково было предыдущее значение во время применяемой функции. Я полагаю, именно поэтому modifyIORef не имеет такого дополнительного возвращаемого значения (поскольку такие обычаи modifyIORef с этим возвращаемым значением, вероятно, следует использовать atomicModifyIORef в любом случае. Я по-прежнему заинтересован в ответ на (1), хотя.

ответ

10

atomicModifyIORef принимает r :: IORef a и функция f :: a -> (a, b) и делает следующее:..

Он считывает значение из r и применяет f к этому значению, уступая (a',b) Тогда r обновляется с новым значением a' в то время как b является возвращаемым значением этого чтения и доступ на запись выполняется атомарно.

Конечно, этот атомизм работает только в том случае, если все Доступ к r осуществляется через atomicModifyIORef. Обратите внимание, что вы можете найти эту информацию, посмотрев на источник [1].

[1] http://hackage.haskell.org/packages/archive/base/latest/doc/html/src/Data-IORef.html#atomicModifyIORef

+3

Выполняет ли блокировку или оптимистично? Версия GHC, похоже, называет примитив GHC. – Clinton

+1

Обратите внимание, что из-за лени, 'atomicModifyIORef' только должен изменить текущее значение, чтобы указать на thunk, при этом фактическая работа задерживается до тех пор, пока она не будет прочитана в более позднее время. AFAIK, он компилируется на что-то вроде CAS на большинстве платформ. – hammar

+10

Оптимистичный, через замкнутый обмен (cas). https://github.com/ghc/ghc/blob/45740c29b24ea78b885d3b9f737a8bdc00265f7c/rts/PrimOps.cmm#L364 –

27

ли ждать его на замок, или оптимистически попытаться повторить, если есть утверждение (как TVAR).

atomicModifyIORef использует инструкцию блокировки на базовой аппаратной архитектуре, на которой вы находитесь, чтобы поменять указатель на выделенный объект Haskell в атомном режиме.

На x86 он использует Cas intruction, подвергаются как примитив к языку через 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 
... 
} 

То есть, он будет пытаться сделать своп , и повторите попытку.

Реализация КАС как примитив показано, как мы спускаемся к металлу:

/* 
* 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, на других архитектурах различные механизмы будут используемый. Время выполнения повторится.