Skip to content

Instantly share code, notes, and snippets.

@AndrasKovacs
Last active August 4, 2021 20:00
Show Gist options
  • Save AndrasKovacs/d39105a103abc69a035e6c6c5e176880 to your computer and use it in GitHub Desktop.
Save AndrasKovacs/d39105a103abc69a035e6c6c5e176880 to your computer and use it in GitHub Desktop.
{-# language ScopedTypeVariables, RankNTypes, BlockArguments, TypeApplications,
LambdaCase #-}
module Lib
( foldr
) where
import Data.Foldable (foldl')
import Prelude hiding (foldr)
import Control.Concurrent.MVar
import Control.Concurrent
import System.IO.Unsafe
forEach :: Foldable t => t a -> (a -> IO ()) -> IO ()
forEach ta f =
foldl' (\b a -> seq (unsafeDupablePerformIO (f a)) b) (pure ()) ta
foldr :: forall t a b. Foldable t => (a -> b -> b) -> b -> t a -> b
foldr f b ta = unsafeDupablePerformIO do
elems <- newEmptyMVar @(Maybe a)
next <- newEmptyMVar @()
forkIO do
forEach ta \a -> putMVar elems (Just a) >> takeMVar next
putMVar elems Nothing
let loop f b = takeMVar elems >>= \case
Nothing -> pure b
Just a -> do
b' <- unsafeInterleaveIO (putMVar next () >> loop f b)
pure (f a b')
loop f b
@treeowl
Copy link

treeowl commented Aug 4, 2021

The outermost unsafeDupablePerformIO looks fine, as does the one in the producer thread. But the one in loop looks shady. f could fork multiple threads, each of which force b'.

@treeowl
Copy link

treeowl commented Aug 4, 2021

So I think you should NOINLINE b' and use unsafePerformIO to calculate it.

@treeowl
Copy link

treeowl commented Aug 4, 2021

Or just use unsafeInterleaveIO there, actually...

@AndrasKovacs
Copy link
Author

I agree it wasn't thread-safe. Didn't need impure f for that though, it would've been enough to share a toList result between threads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment