[GHC] #13052: unsafePerformIO duped on multithread if within the same IO thunk

GHC ghc-devs at haskell.org
Sun Jan 1 16:47:17 UTC 2017


#13052: unsafePerformIO duped on multithread if within the same IO thunk
-------------------------------------+-------------------------------------
           Reporter:  gelisam        |             Owner:
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:
          Component:  Compiler       |           Version:  8.0.2-rc2
           Keywords:                 |  Operating System:  MacOS X
       Architecture:  x86_64         |   Type of failure:  Incorrect result
  (amd64)                            |  at runtime
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 Unlike `unsafeDupablePerformIO`, an `unsafePerformIO` block is not
 supposed to be executed more than once when two threads race to evaluate
 it, and yet the following program detects that the counter is sometimes
 incremented twice:

 {{{#!hs
 {-# LANGUAGE BangPatterns #-}
 {-# OPTIONS -O0 -threaded -rtsopts -with-rtsopts=-N #-}
 module Main where
 import Control.Concurrent
 import System.IO.Unsafe

 runThreads :: IO () -> IO () -> IO ()
 runThreads body1 body2 = do
   var1 <- newEmptyMVar
   var2 <- newEmptyMVar
   _ <- forkIO $ do { !_ <- body1; putMVar var1 () }
   _ <- forkIO $ do { !_ <- body2; putMVar var2 () }
   takeMVar var1
   takeMVar var2

 main :: IO ()
 main = do
   counter <- newMVar (0 :: Int)
   let sharedThunk = unsafePerformIO
                   $ modifyMVar_ counter (return . (+1))
   let sharedIO = return sharedThunk
   _ <- runThreads sharedIO sharedIO
   n <- takeMVar counter
   if n == 1 then main else print n
 }}}

 Note that optimizations are turned off, so this isn't due to inlining. In
 fact, if I inline `sharedIO` and write

 {{{#!hs
   _ <- runThreads (return sharedThunk) (return sharedThunk)
 }}}

 instead, the problem disappears. So it seems that in order to reproduce
 the problem, two threads must race to evaluate an IO thunk containing an
 `unsafePerformIO` block; a race to evaluate the `unsafePerformIO` block is
 not sufficient.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13052>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list