packages feed

keid-core-0.1.0.0: src/Engine/Events/Sink.hs

module Engine.Events.Sink
  ( Sink(..)
  , spawn
  ) where

import RIO

import Control.Concurrent.Chan.Unagi qualified as Unagi
import Control.Exception (AsyncException(ThreadKilled))
import UnliftIO.Concurrent (forkFinally, killThread)
import UnliftIO.Resource (ReleaseKey)
import UnliftIO.Resource qualified as Resource

import Engine.Types (StageRIO)

newtype Sink event st = Sink
  { signal :: event -> StageRIO st ()
  }

spawn
  :: (event -> StageRIO rs ())
  -> StageRIO rs (ReleaseKey, Sink event rs)
spawn handleEvent = do
  (eventsIn, eventsOut) <- liftIO Unagi.newChan
  let sink = Sink \event -> liftIO (Unagi.writeChan eventsIn event)

  let
    handler = forever $
      liftIO (Unagi.readChan eventsOut) >>= handleEvent

  tid <- forkFinally handler
    \case
      Left exc ->
        case fromException exc of
          Just ThreadKilled ->
            logDebug "Event thread killed"
          _others ->
            logError $ "Event thread crashed: " <> displayShow exc
      Right () ->
        logWarn "Event thread exited prematurely"
  key <- Resource.register $ killThread tid
  pure (key, sink)