    module Control.AutoUpdate.Thread (
        -- * Creation
        mkAutoUpdate,
        mkAutoUpdateWithModify,
    ) where

    import Control.Concurrent (forkIO, threadDelay)
    import Control.Concurrent.MVar (
        newEmptyMVar,
        putMVar,
        readMVar,
        takeMVar,
        tryPutMVar,
     )
    import Control.Exception (
        SomeException,
        catch,
        mask_,
        throw,
        try,
     )
    import Control.Monad (void)
    import Data.IORef (newIORef, readIORef, writeIORef)
    import Data.Maybe (fromMaybe)
    import GHC.Conc.Sync (labelThread)

    import Control.AutoUpdate.Types

    -- | Generate an action which will either read from an automatically
    -- updated value, or run the update action in the current thread.
    --
    -- @since 0.1.0
    mkAutoUpdate :: UpdateSettings a -> IO (IO a)
    mkAutoUpdate :: forall a. UpdateSettings a -> IO (IO a)
    mkAutoUpdate UpdateSettings a
    us = UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    forall a. UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    mkAutoUpdateHelper UpdateSettings a
    us Maybe (a -> IO a)
    forall a. Maybe a
    Nothing

    -- | Generate an action which will either read from an automatically
    -- updated value, or run the update action in the current thread if
    -- the first time or the provided modify action after that.
    --
    -- @since 0.1.4
    mkAutoUpdateWithModify :: UpdateSettings a -> (a -> IO a) -> IO (IO a)
    mkAutoUpdateWithModify :: forall a. UpdateSettings a -> (a -> IO a) -> IO (IO a)
    mkAutoUpdateWithModify UpdateSettings a
    us a -> IO a
    f = UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    forall a. UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    mkAutoUpdateHelper UpdateSettings a
    us ((a -> IO a) -> Maybe (a -> IO a)
    forall a. a -> Maybe a
    Just a -> IO a
    f)

    mkAutoUpdateHelper :: UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    mkAutoUpdateHelper :: forall a. UpdateSettings a -> Maybe (a -> IO a) -> IO (IO a)
    mkAutoUpdateHelper UpdateSettings a
    us Maybe (a -> IO a)
    updateActionModify = do
        -- A baton to tell the worker thread to generate a new value.
        needsRunning <- IO (MVar ())
    forall a. IO (MVar a)
    newEmptyMVar

        -- The initial response variable. Response variables allow the requesting
        -- thread to block until a value is generated by the worker thread.
        responseVar0 <- newEmptyMVar

        -- The current value, if available. We start off with a Left value
        -- indicating no value is available, and the above-created responseVar0 to
        -- give a variable to block on.
        currRef <- newIORef $ Left responseVar0

        -- This is used to set a value in the currRef variable when the worker
        -- thread exits. In reality, that value should never be used, since the
        -- worker thread exiting only occurs if an async exception is thrown, which
        -- should only occur if there are no references to needsRunning left.
        -- However, this handler will make error messages much clearer if there's a
        -- bug in the implementation.
        let fillRefOnExit IO ()
    f = do
                eres <- IO () -> IO (Either SomeException ())
    forall e a. Exception e => IO a -> IO (Either e a)
    try IO ()
    f
                case eres of
                    Left SomeException
    e ->
                        IORef (Either (MVar a) a) -> Either (MVar a) a -> IO ()
    forall a. IORef a -> a -> IO ()
    writeIORef IORef (Either (MVar a) a)
    currRef (Either (MVar a) a -> IO ()) -> Either (MVar a) a -> IO ()
    forall a b. (a -> b) -> a -> b
    $
                            [Char] -> Either (MVar a) a
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> Either (MVar a) a) -> [Char] -> Either (MVar a) a
    forall a b. (a -> b) -> a -> b
    $
                                [Char]
    "Control.AutoUpdate.mkAutoUpdate: worker thread exited with exception: "
                                    [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ SomeException -> [Char]
    forall a. Show a => a -> [Char]
    show (SomeException
    e :: SomeException)
                    Right () ->
                        IORef (Either (MVar a) a) -> Either (MVar a) a -> IO ()
    forall a. IORef a -> a -> IO ()
    writeIORef IORef (Either (MVar a) a)
    currRef (Either (MVar a) a -> IO ()) -> Either (MVar a) a -> IO ()
    forall a b. (a -> b) -> a -> b
    $
                            [Char] -> Either (MVar a) a
    forall a. HasCallStack => [Char] -> a
    error ([Char] -> Either (MVar a) a) -> [Char] -> Either (MVar a) a
    forall a b. (a -> b) -> a -> b
    $
                                [Char]
    "Control.AutoUpdate.mkAutoUpdate: worker thread exited normally, "
                                    [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "which should be impossible due to usage of infinite loop"

        -- fork the worker thread immediately. Note that we mask async exceptions,
        -- but *not* in an uninterruptible manner. This will allow a
        -- BlockedIndefinitelyOnMVar exception to still be thrown, which will take
        -- down this thread when all references to the returned function are
        -- garbage collected, and therefore there is no thread that can fill the
        -- needsRunning MVar.
        --
        -- Note that since we throw away the ThreadId of this new thread and never
        -- calls myThreadId, normal async exceptions can never be thrown to it,
        -- only RTS exceptions.
        tid <- mask_ $ forkIO $ fillRefOnExit $ do
            -- This infinite loop makes up out worker thread. It takes an a
            -- responseVar value where the next value should be putMVar'ed to for
            -- the benefit of any requesters currently blocked on it.
            let loop MVar a
    responseVar Maybe a
    maybea = do
                    -- block until a value is actually needed
                    MVar () -> IO ()
    forall a. MVar a -> IO a
    takeMVar MVar ()
    needsRunning

                    -- new value requested, so run the updateAction
                    a <- IO a -> IO a
    forall a. IO a -> IO a
    catchSome (IO a -> IO a) -> IO a -> IO a
    forall a b. (a -> b) -> a -> b
    $ IO a -> Maybe (IO a) -> IO a
    forall a. a -> Maybe a -> a
    fromMaybe (UpdateSettings a -> IO a
    forall a. UpdateSettings a -> IO a
    updateAction UpdateSettings a
    us) (Maybe (a -> IO a)
    updateActionModify Maybe (a -> IO a) -> Maybe a -> Maybe (IO a)
    forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Maybe a
    maybea)

                    -- we got a new value, update currRef and lastValue
                    writeIORef currRef $ Right a
                    putMVar responseVar a

                    -- delay until we're needed again
                    threadDelay $ updateFreq us

                    -- delay's over. create a new response variable and set currRef
                    -- to use it, so that the next requester will block on that
                    -- variable. Then loop again with the updated response
                    -- variable.
                    responseVar' <- newEmptyMVar
                    writeIORef currRef $ Left responseVar'
                    loop responseVar' (Just a)

            -- Kick off the loop, with the initial responseVar0 variable.
            loop responseVar0 Nothing
        labelThread tid $ updateThreadName us
        return $ do
            mval <- readIORef currRef
            case mval of
                Left MVar a
    responseVar -> do
                    -- no current value, force the worker thread to run...
                    IO Bool -> IO ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (IO Bool -> IO ()) -> IO Bool -> IO ()
    forall a b. (a -> b) -> a -> b
    $ MVar () -> () -> IO Bool
    forall a. MVar a -> a -> IO Bool
    tryPutMVar MVar ()
    needsRunning ()

                    -- and block for the result from the worker
                    MVar a -> IO a
    forall a. MVar a -> IO a
    readMVar MVar a
    responseVar
                -- we have a current value, use it
                Right a
    val -> a -> IO a
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    val

    -- | Turn a runtime exception into an impure exception, so that all 'IO'
    -- actions will complete successfully. This simply defers the exception until
    -- the value is forced.
    catchSome :: IO a -> IO a
    catchSome :: forall a. IO a -> IO a
    catchSome IO a
    act = IO a -> (SomeException -> IO a) -> IO a
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    Control.Exception.catch IO a
    act ((SomeException -> IO a) -> IO a)
    -> (SomeException -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ \SomeException
    e -> a -> IO a
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (a -> IO a) -> a -> IO a
    forall a b. (a -> b) -> a -> b
    $ SomeException -> a
    forall a e. (HasCallStack, Exception e) => e -> a
    throw (SomeException
    e :: SomeException)