setupCommandLoop :: Maybe FilePath -> [String] -> IO (Input CommandObj, Output ClientDirective, Input ClientDirective)
setupCommandLoop cabal initialGhcOpts = do
  (serverInput,serverOutput) <- spawn Unbounded
  (clientInput,clientOutput) <- spawn Unbounded
  warnings <- newIORef True
  fileName <- newIORef $ GHC.fsLit ""
  filePath <- newIORef $ GHC.fsLit ""
  errors <- newIORef S.empty
  cabalOptions <- cabalMiscOptions
  let settings = Settings cabal warnings fileName filePath errors cabalOptions

  finishGHCstartup <- Event.new

  t <- myThreadId
  let exception (SomeException e) = GHC.liftIO $ atomically $ do
        void $ send serverInput $ ClientStderr $ "[setupCommandLoop] Unhandled GHC Exception: " ++ show e
        void $ send serverInput $ ClientExit $ ExitFailure 2

  _ <- forkIO $ GHC.runGhc (Just GHC.Paths.libdir) $ do
       GHC.liftIO $ Event.signal finishGHCstartup
       forever $ Exception.ghandle exception $
         runProxy $ runReaderK settings $ evalStateK (defaultOptions & ghcOpts .~ initialGhcOpts) $
           hoist GHC.liftIO . recvS clientOutput >-> commandLoop >-> hoist GHC.liftIO . sendD serverInput

  -- for some reason GHC changes the default ^C handlers. They don't work when used in a thread, so we reset
  -- them here, after GHC started to make sure we override GHC's handler.
  Event.wait finishGHCstartup
  _ <- installHandler sigINT (Catch $ throwTo t UserInterrupt) Nothing
  _ <- installHandler sigTERM (Catch $ throwTo t UserInterrupt) Nothing

  return (clientInput,serverOutput,serverInput)
setupCommandLoop :: Maybe FilePath -> [String] -> IO (Input CommandObj, Output ClientDirective, Input ClientDirective)
setupCommandLoop cabal initialGhcOpts = do
  (serverInput,serverOutput) <- spawn Unbounded
  (clientInput,clientOutput) <- spawn Unbounded
  warnings <- newIORef True
  fileName <- newIORef $ GHC.fsLit ""
  filePath <- newIORef $ GHC.fsLit ""
  errors <- newIORef S.empty
  cabalOptions <- cabalMiscOptions
  let settings = Settings cabal warnings fileName filePath errors cabalOptions

  finishGHCstartup <- Event.new

  t <- myThreadId
  let exception (SomeException e) = GHC.liftIO $ atomically $ do
        void $ send serverInput $ ClientStderr $ "[setupCommandLoop] Unhandled GHC Exception: " ++ show e
        void $ send serverInput $ ClientExit $ ExitFailure 2

  _ <- forkIO $ GHC.runGhc (Just GHC.Paths.libdir) $ do
       GHC.liftIO $ Event.signal finishGHCstartup
       forever $ Exception.ghandle exception $
         runProxy $ runReaderK settings $ evalStateK (defaultOptions & ghcOpts .~ initialGhcOpts) $
           hoist GHC.liftIO . recvS clientOutput >-> commandLoop >-> hoist GHC.liftIO . sendD serverInput

  -- for some reason GHC changes the default ^C handlers. They don't work when used in a thread, so we reset
  -- them here, after GHC started to make sure we override GHC's handler.
  Event.wait finishGHCstartup
  _ <- installHandler sigINT (Catch $ throwTo t UserInterrupt) Nothing
  _ <- installHandler sigTERM (Catch $ throwTo t UserInterrupt) Nothing

  return (clientInput,serverOutput,serverInput)
setupCommandLoop cabal initialGhcOpts = do
  (serverInput,serverOutput) <- spawn Unbounded
  (clientInput,clientOutput) <- spawn Unbounded
  warnings <- newIORef True
  fileName <- newIORef $ GHC.fsLit ""
  filePath <- newIORef $ GHC.fsLit ""
  errors <- newIORef S.empty
  cabalOptions <- cabalMiscOptions
  let settings = Settings cabal warnings fileName filePath errors cabalOptions

  finishGHCstartup <- Event.new

  t <- myThreadId
  let exception (SomeException e) = GHC.liftIO $ atomically $ do
        void $ send serverInput $ ClientStderr $ "[setupCommandLoop] Unhandled GHC Exception: " ++ show e
        void $ send serverInput $ ClientExit $ ExitFailure 2

  _ <- forkIO $ GHC.runGhc (Just GHC.Paths.libdir) $ do
       GHC.liftIO $ Event.signal finishGHCstartup
       forever $ Exception.ghandle exception $
         runProxy $ runReaderK settings $ evalStateK (defaultOptions & ghcOpts .~ initialGhcOpts) $
           hoist GHC.liftIO . recvS clientOutput >-> commandLoop >-> hoist GHC.liftIO . sendD serverInput

  -- for some reason GHC changes the default ^C handlers. They don't work when used in a thread, so we reset
  -- them here, after GHC started to make sure we override GHC's handler.
  Event.wait finishGHCstartup
  _ <- installHandler sigINT (Catch $ throwTo t UserInterrupt) Nothing
  _ <- installHandler sigTERM (Catch $ throwTo t UserInterrupt) Nothing

  return (clientInput,serverOutput,serverInput)