Opened 9 years ago

Closed 8 years ago

#5238 closed bug (fixed)

throwSTM+catchSTM pollutes the masking state

Reported by: mikhail.vorozhtsov Owned by: simonmar
Priority: high Milestone: 7.4.1
Component: Runtime System Version: 7.1
Keywords: stm Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

The following program prints "(Unmasked,MaskedUninterruptible)"

{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Control.Exception
import Control.Concurrent.STM
import GHC.Conc (STM(..))
import GHC.Prim (getMaskingState#)

getMaskingStateSTM = STM $ \s → case getMaskingState# s of
 (# s', i #) -> (# s', case i of 0# → Unmasked
                                 1# → MaskedUninterruptible
                                 _  → MaskedInterruptible #)

main = do
  mss ← atomically $ do
    ms1 ← getMaskingStateSTM
    (throwSTM Overflow) `catchSTM` (\(e ∷ SomeException) → return ())
    ms2 ← getMaskingStateSTM
    return (ms1, ms2)
  putStrLn $ show mss

I would be nice to have (un)maskAsyncExceptions+retry supported too, currenly

maskSTM (STM stm) = STM $ maskAsyncExceptions# stm

main = do
  mss ← atomically $ do
    ms1 ← getMaskingStateSTM
    maskSTM retry `orElse` return ()
    ms2 ← getMaskingStateSTM
    return (ms1, ms2)
  putStrLn $ show mss

prints "(Unmasked,MaskedInterruptible)"

Change History (6)

comment:1 Changed 9 years ago by mikhail.vorozhtsov

And it actually escapes to IO:

$ cat E.hs
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Control.Exception
import Control.Concurrent.STM

main = do
  ms1 ← getMaskingState
  atomically $ (throwSTM Overflow) `catchSTM`
               (\(e ∷ SomeException) → return ())
  ms2 ← getMaskingState
  putStrLn $ show (ms1, ms2)

$ runhaskell E.hs
(Unmasked,MaskedInterruptible)

comment:2 Changed 9 years ago by simonmar

Milestone: 7.2.1
Owner: set to simonmar
Priority: normalhigh

hmm, I can see where the bug is, but I need to think about what behaviour we want here. Is it sensible for catchSTM to catch asynchronous exceptions, and to be able to mask inside STM?

comment:3 in reply to:  2 Changed 9 years ago by mikhail.vorozhtsov

Replying to simonmar:

Is it sensible for catchSTM to catch asynchronous exceptions, and to be able to mask inside STM?

Yes. I intend to use it like this:

mask $ \restore → do
  ioBefore
  atomically $ restore $ aRatherLongTransactionWithRetries
  ioAfter

where mask has type

MonadException µ η ⇒ ((∀ μ' η' β . MonadException μ' η' ⇒ μ' β → μ' β) → μ α) → μ α

(which basically means that it can restore the masking state in both IO and STM, as well as in any stack of transformers which has one of them as the base, via the monad-control package machinery).

This way it would be guaranteed that if the computation is interrupted then the transaction is not committed.

comment:4 Changed 8 years ago by igloo

Milestone: 7.2.17.4.1

comment:5 Changed 8 years ago by marlowsd@…

commit 66265ae0970b9d36b8847b75e176f0de4844690e

Author: Simon Marlow <marlowsd@gmail.com>
Date:   Wed Nov 16 14:21:49 2011 +0000

    Fix trashing of the masking state in STM (#5238)

 rts/Exception.cmm |   39 +++++++++++++++++++++------------------
 1 files changed, 21 insertions(+), 18 deletions(-)

comment:6 Changed 8 years ago by simonmar

Resolution: fixed
Status: newclosed

I've fixed the bug, but I'm not planning to do anything about add masking support to STM for 7.4.1.

It's still not clear to me that allowing mask/restore within the STM monad is the right thing, though I do understand that in the example given above there's no way to unmask exceptions within the STM transaction but not including the commit. Let's take this one to the mailing list.

Note: See TracTickets for help on using tickets.