[Git][ghc/ghc][wip/inline-unsafeDupableInterleaveIO] Allow unsafeDupableInterleaveIO to inline

Matthew Craven (@clyring) gitlab at gitlab.haskell.org
Mon Jan 15 23:38:20 UTC 2024



Matthew Craven pushed to branch wip/inline-unsafeDupableInterleaveIO at Glasgow Haskell Compiler / GHC


Commits:
1d0136eb by Matthew Craven at 2024-01-15T18:37:32-05:00
Allow unsafeDupableInterleaveIO to inline

This requires preventing unsafeInterleaveIO from inlining,
for reasons described in the relevant Note, which has been
renamed and updated.

- - - - -


6 changed files:

- libraries/base/src/GHC/IO/Unsafe.hs
- libraries/base/src/GHC/ST.hs
- + libraries/base/tests/T5859.hs
- + libraries/base/tests/T5859.stdout
- libraries/base/tests/all.T
- testsuite/tests/lib/base/all.T


Changes:

=====================================
libraries/base/src/GHC/IO/Unsafe.hs
=====================================
@@ -150,29 +150,28 @@ When passed a value of type @IO a@, the 'IO' will only be performed
 when the value of the @a@ is demanded.  This is used to implement lazy
 file reading, see 'System.IO.hGetContents'.
 -}
-{-# INLINE unsafeInterleaveIO #-}
+{-# NOINLINE unsafeInterleaveIO #-}
+-- See Note [unsafeInterleaveIO should not be inlined]
 unsafeInterleaveIO :: IO a -> IO a
 unsafeInterleaveIO m = unsafeDupableInterleaveIO (noDuplicate >> m)
 
--- Note [unsafeDupableInterleaveIO should not be inlined]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Note [unsafeInterleaveIO should not be inlined]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 --
 -- We used to believe that INLINE on unsafeInterleaveIO was safe,
 -- because the state from this IO thread is passed explicitly to the
 -- interleaved IO, so it cannot be floated out and shared.
 --
--- HOWEVER, if the compiler figures out that r is used strictly here,
--- then it will eliminate the thunk and the side effects in m will no
--- longer be shared in the way the programmer was probably expecting,
--- but can be performed many times.  In #5943, this broke our
--- definition of fixIO, which contains
+-- HOWEVER, due to the state hack (on by default), the compiler may
+-- float the key thunk 'r' from unsafeDupableInterleaveIO into a
+-- counterfeit one-shot lambda, thus duplicating it.  Or it may just
+-- wrongly conclude that the thunk is single-entry, which might keep
+-- noDuplicate# from working since that looks for update frames on the
+-- stack. This came up in #5859, and in #5943 this broke our
+-- definition of fixIO, which at the time contained
 --
 --    ans <- unsafeInterleaveIO (takeMVar m)
 --
--- after inlining, we lose the sharing of the takeMVar, so the second
--- time 'ans' was demanded we got a deadlock.  We could fix this with
--- a readMVar, but it seems wrong for unsafeInterleaveIO to sometimes
--- share and sometimes not (plus it probably breaks the noDuplicate).
 -- So now, we do not inline unsafeDupableInterleaveIO.
 
 {-|
@@ -184,9 +183,6 @@ The computation may be performed multiple times by different threads,
 possibly at the same time. To ensure that the computation is performed
 only once, use 'unsafeInterleaveIO' instead.
 -}
-
-{-# NOINLINE unsafeDupableInterleaveIO #-}
--- See Note [unsafeDupableInterleaveIO should not be inlined]
 unsafeDupableInterleaveIO :: IO a -> IO a
 unsafeDupableInterleaveIO (IO m)
   = IO ( \ s -> let


=====================================
libraries/base/src/GHC/ST.hs
=====================================
@@ -97,7 +97,8 @@ noDuplicateST = ST $ \s -> (# noDuplicate# s, () #)
 -- | 'unsafeInterleaveST' allows an 'ST' computation to be deferred
 -- lazily.  When passed a value of type @ST a@, the 'ST' computation will
 -- only be performed when the value of the @a@ is demanded.
-{-# INLINE unsafeInterleaveST #-}
+{-# NOINLINE unsafeInterleaveST #-}
+-- See Note [unsafeInterleaveIO should not be inlined] in GHC.IO.Unsafe
 unsafeInterleaveST :: ST s a -> ST s a
 unsafeInterleaveST m = unsafeDupableInterleaveST (noDuplicateST >> m)
 
@@ -109,9 +110,6 @@ unsafeInterleaveST m = unsafeDupableInterleaveST (noDuplicateST >> m)
 -- possibly at the same time. To prevent this, use 'unsafeInterleaveST' instead.
 --
 -- @since 4.11
-{-# NOINLINE unsafeDupableInterleaveST #-}
--- See Note [unsafeDupableInterleaveIO should not be inlined]
--- in GHC.IO.Unsafe
 unsafeDupableInterleaveST :: ST s a -> ST s a
 unsafeDupableInterleaveST (ST m) = ST ( \ s ->
     let


=====================================
libraries/base/tests/T5859.hs
=====================================
@@ -0,0 +1,12 @@
+import Control.Concurrent
+import Control.Exception (evaluate)
+import Control.Monad
+import System.IO.Unsafe
+
+main :: IO ()
+main = do
+  x <- unsafeInterleaveIO $ putStrLn "eval"
+  -- Since this is 'unsafeInterleaveIO' and not 'unsafeDupableInterleaveIO',
+  -- this program must print "eval" only once
+  replicateM_ 1000 $ forkIO $ evaluate x >> return ()
+  threadDelay 1000000


=====================================
libraries/base/tests/T5859.stdout
=====================================
@@ -0,0 +1 @@
+eval


=====================================
libraries/base/tests/all.T
=====================================
@@ -1,3 +1,5 @@
+# See also the tests in testsuite/tests/lib/base/
+
 import string
 import re
 
@@ -163,7 +165,11 @@ test('T2528', normal, compile_and_run, [''])
 #     so let's normalise the output.
 test('T4006', [normalise_fun(normalise_quotes), req_process], compile_and_run, [''])
 
-test('T5943', normal, compile_and_run, [''])
+# These two tests should have "-fstate-hack" (to be explicit),
+# but that flag apparently doesn't exist for some reason
+test('T5859', normal, compile_and_run, ['-O'])
+test('T5943', normal, compile_and_run, ['-O'])
+
 test('T5962', normal, compile_and_run, [''])
 test('T7034', normal, compile_and_run, [''])
 


=====================================
testsuite/tests/lib/base/all.T
=====================================
@@ -1,3 +1,5 @@
+# See also the tests in libraries/base/tests/
+
 test('DataTypeOrd', normal, compile_and_run, [''])
 test('T16586', normal, compile_and_run, ['-O2'])
 # Event-manager not supported on Windows



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1d0136eb2746c81161386722e34b0ba8733f89e7

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1d0136eb2746c81161386722e34b0ba8733f89e7
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240115/45887658/attachment-0001.html>


More information about the ghc-commits mailing list