{-# LANGUAGE NumDecimals     #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies    #-}
{-# OPTIONS_HADDOCK not-home #-}
module Polysemy.Internal.Forklift where
import qualified Control.Concurrent.Async as A
import           Control.Concurrent.Chan.Unagi
import           Control.Concurrent.MVar
import           Control.Exception
import           Polysemy.Internal
import           Polysemy.Internal.Union
data Forklift r = forall a. Forklift
  { responseMVar :: MVar a
  , request      :: Union r (Sem r) a
  }
runViaForklift
    :: Member (Embed IO) r
    => InChan (Forklift r)
    -> Sem r a
    -> IO a
runViaForklift chan = usingSem $ \u -> do
  case prj u of
    Just (Weaving (Embed m) s _ ex _) ->
      ex . (<$ s) <$> m
    _ -> do
      mvar <- newEmptyMVar
      writeChan chan $ Forklift mvar u
      takeMVar mvar
{-# INLINE runViaForklift #-}
withLowerToIO
    :: Member (Embed IO) r
    => ((forall x. Sem r x -> IO x) -> IO () -> IO a)
       
       
       
    -> Sem r a
withLowerToIO action = do
  (inchan, outchan) <- embed newChan
  signal <- embed newEmptyMVar
  res <- embed $ A.async $ do
    a <- action (runViaForklift inchan)
                (putMVar signal ())
          `finally` (putMVar signal ())
    pure a
  let me = do
        raced <- embed $ A.race (takeMVar signal) $ readChan outchan
        case raced of
          Left () -> embed $ A.wait res
          Right (Forklift mvar req) -> do
            resp <- liftSem req
            embed $ putMVar mvar $ resp
            me_b
      {-# INLINE me #-}
      me_b = me
      {-# NOINLINE me_b #-}
  me