Skip to content

Commit

Permalink
Fix a bug in the handling of nested orElse
Browse files Browse the repository at this point in the history
Exposed by the following snippet, courtesy of Bas van Dijk and Patrick
Palka on libraries@haskell.org:

import Control.Concurrent.STM
main = do
  x <- atomically $ do
         t <- newTVar 1
         writeTVar t 2
         ((readTVar t >> retry) `orElse` return ()) `orElse` return ()
         readTVar t
  print x
  • Loading branch information
simonmar committed Dec 10, 2012
1 parent 332e681 commit f184d9c
Showing 1 changed file with 21 additions and 3 deletions.
24 changes: 21 additions & 3 deletions rts/STM.c
Original file line number Diff line number Diff line change
Expand Up @@ -1460,10 +1460,28 @@ StgBool stmCommitNestedTransaction(Capability *cap, StgTRecHeader *trec) {

StgTVar *s;
s = e -> tvar;
if (entry_is_update(e)) {

// Careful! We might have a read entry here that we don't want
// to spam over the update entry in the enclosing TRec. e.g. in
//
// t <- newTVar 1
// writeTVar t 2
// ((readTVar t >> retry) `orElse` return ()) `orElse` return ()
//
// - the innermost txn first aborts, giving us a read-only entry
// with e->expected_value == e->new_value == 1
// - the inner orElse commits into the outer orElse, which
// lands us here. If we unconditionally did
// merge_update_into(), then we would overwrite the outer
// TRec's update, so we must check whether the entry is an
// update or not, and if not, just do merge_read_into.
//
if (entry_is_update(e)) {
unlock_tvar(cap, trec, s, e -> expected_value, FALSE);
}
merge_update_into(cap, et, s, e -> expected_value, e -> new_value);
merge_update_into(cap, et, s, e -> expected_value, e -> new_value);
} else {
merge_read_into(cap, et, s, e -> expected_value);
}
ACQ_ASSERT(s -> current_value != (StgClosure *)trec);
});
} else {
Expand Down

0 comments on commit f184d9c

Please sign in to comment.