Por exemplo, considere esta função, que poderia ser usada em um WM para permitir mover uma janela de uma área de trabalho para outra em uma determinada tela,
moveWindowSTM :: Display -> Window -> Desktop -> Desktop -> STM ()
moveWindowSTM disp win a b = do
wa <- readTVar ma
wb <- readTVar mb
writeTVar ma (Set.delete win wa)
writeTVar mb (Set.insert win wb)
where
ma = disp ! a
mb = disp ! b
e obviamente seu IO
invólucro,
moveWindow :: Display -> Window -> Desktop -> Desktop -> IO ()
moveWindow disp win a b = atomically $ moveWindowSTM disp win a b
e então assumir que
- nossa transação é bem-sucedida na terceira tentativa,
- porque na primeira tentativa ele é invalidado por outra transação simultânea sendo confirmada que altera o valor interno
ma
logo após o términowa <- readTVar ma
da nossa transação, - e na segunda tentativa ele é invalidado por outra transação simultânea que altera o conteúdo de uma ou ambas as transações
ma
emb
logo depoiswriteTVar ma (Set.delete win wa)
da nossa transação.
Como o log de transações evoluiria nesse caso e em que ponto a validação falharia?
O trecho relevante de Parallel and Concurrent Programming in Haskell de Simon Marlow está abaixo (mas informações semelhantes estão disponíveis no artigo Beautiful concurrency de Simon Peyton Jones):
Uma transação STM funciona acumulando um log de
readTVar
operaçõeswriteTVar
que ocorreram até o momento durante a transação. O log é usado de três maneiras:
Ao armazenar
writeTVar
as operações no log em vez de aplicá-las imediatamente à memória principal, descartar os efeitos de uma transação é fácil; simplesmente descartamos o log. Portanto, abortar uma transação tem um custo fixo pequeno.Cada um
readTVar
deve percorrer o log para verificar se oTVar
foi escrito por umwriteTVar
. Portanto,readTVar
é uma operação O(n) no comprimento do log.Como o log contém um registro de todas as
readTVar
operações, ele pode ser usado para descobrir o conjunto completo deTVars
leituras durante a transação, o que precisamos saber para implementarretry
.Quando uma transação chega ao fim, a implementação do STM compara o log com o conteúdo da memória. Se o conteúdo atual da memória corresponder aos valores lidos por
readTVar
, os efeitos da transação são confirmados na memória; caso contrário, o log é descartado e a transação é executada novamente desde o início. Esse processo ocorre atomicamente, bloqueando todos osTVars
envolvidos na transação durante sua duração. A implementação do STM no GHC não utiliza bloqueios globais; apenas osTVars
envolvidos na transação são bloqueados durante o commit, de modo que transações que operam em conjuntos disjuntos deTVars
podem prosseguir sem interferência.