[GHC] #8680: In STM: Variables only in left branch of orElse can invalidate the right branch transaction
GHC
ghc-devs at haskell.org
Fri Jan 31 03:14:50 UTC 2014
#8680: In STM: Variables only in left branch of orElse can invalidate the right
branch transaction
--------------------------------------------+------------------------------
Reporter: jberryman | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.6.3
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: Runtime performance bug | Unknown/Multiple
Test Case: | Difficulty: Unknown
Blocking: | Blocked By:
| Related Tickets:
--------------------------------------------+------------------------------
Changes (by fryguybob):
* cc: fryguybob@… (added)
Comment:
As [comment:2 ezyang] said, this is the expected behavior. We only visit
the right branch if the left branch reaches retry. While the effects of
the left branch are discarded, the outcome is not. When the whole
transaction commits, it will check that the outcome of the left branch is
still the same by checking that the reads from the left branch have not
changed.
This becomes important if the right branch reaches `retry` (and this is a
top-level `orElse`). When the transaction blocks, it needs to be added to
the watch list for all the `TVar`s read, including the branches that we
discarded as it may be the case that an earlier branch will allow the
transaction to make progress. Before we can block we must validate that
we are not blocking due to reading inconsistent data. Otherwise we could
block and miss the chance to wake up.
While I think a nondeterministic `orElse` should be possible, it would be
tricky to get it right and with the fairness that we would clearly want.
I'm not sure what are the right interactions with nesting and blocking. A
particular branch could reach `retry` due to inconsistent reads. Do we
still propagate the retry up, potentially taking another branch at a
higher level? Do we block on this faulty data, or validate the discarded
reads first? If found invalid do we start all over, or do we try
inconsistent branches again? If so in what order? Perhaps there is some
wisdom to be gleaned from this work: http://www.cs.rit.edu/~mtf/research
/tx-events/IFL11/techrpt.pdf
If you only care about a top-level chain of `orElse`s as in the example,
you can sort of accomplish what you want now with something like this:
{{{
#!haskell
atomicallyOneOf :: [STM a] -> IO a
atomicallyOneOf ts = go (cycle (map run ts))
where
go (t:ts) = do
v <- t
case v of
Nothing -> go ts
Just a -> return a
run t = atomically ((Just <$> t) `orElse` return Nothing)
}}}
But this will still get "stuck" when it runs across a `t` that is
contending with a repeated faster transaction. If we had a
`tryAtomically` that didn't bother to start again you could do a little
better, but I'm not sure this is a good API to give users in general as
they might reach for it at the wrong time. The problem might be better
addressed in the other direction by avoiding the repeated fast committing
transaction.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8680#comment:4>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list