{-# LANGUAGE CPP #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE FlexibleContexts #-}

module Control.Concurrent.STM.Timeout
  ( registerDelay
  ) where

import Data.Timeout
import Control.Monad (void)
import Control.Monad.Base
import Control.Monad.STM
import Control.Concurrent.STM.TVar hiding (registerDelay)
import GHC.Event (registerTimeout)
#if MIN_VERSION_base(4,7,0)
import GHC.Event (getSystemTimerManager)
#else
import GHC.Event (getSystemEventManager)
#endif

registerDelay  MonadBase IO μ  Timeout  μ (TVar Bool)
registerDelay :: forall (μ :: * -> *). MonadBase IO μ => Timeout -> μ (TVar Bool)
registerDelay Timeout
tt = IO (TVar Bool) -> μ (TVar Bool)
forall α. IO α -> μ α
forall (b :: * -> *) (m :: * -> *) α. MonadBase b m => b α -> m α
liftBase (IO (TVar Bool) -> μ (TVar Bool))
-> IO (TVar Bool) -> μ (TVar Bool)
forall a b. (a -> b) -> a -> b
$
  if Timeout
tt Timeout -> Timeout -> Bool
forall a. Eq a => a -> a -> Bool
== Timeout
instantly
    then Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
True
    else do
      TVar Bool
ttv  Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
False
#if MIN_VERSION_base(4,7,0)
      TimerManager
tmm  IO TimerManager
getSystemTimerManager
#else
      Just tmm  getSystemEventManager
#endif
      let us :: Word64
us = Timeout
tt Timeout -> TimeoutUnit -> Word64
#> TimeoutUnit
MicroSecond
          maxUs :: Word64
maxUs = Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
forall a. Bounded a => a
maxBound  Int)
          us' :: Word64
us' = Word64
maxUs Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
`min` Word64
us
          rearm :: Word64 -> TimeoutCallback
rearm Word64
passed =
            case Word64
us Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
passed of
              Word64
0  STM () -> TimeoutCallback
forall a. STM a -> IO a
atomically (STM () -> TimeoutCallback) -> STM () -> TimeoutCallback
forall a b. (a -> b) -> a -> b
$ TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar TVar Bool
ttv Bool
True
              Word64
left  do
                let us'' :: Word64
us'' = Word64
maxUs Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
`min` Word64
left
                IO TimeoutKey -> TimeoutCallback
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO TimeoutKey -> TimeoutCallback)
-> IO TimeoutKey -> TimeoutCallback
forall a b. (a -> b) -> a -> b
$ TimerManager -> Int -> TimeoutCallback -> IO TimeoutKey
registerTimeout TimerManager
tmm (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
us'') (TimeoutCallback -> IO TimeoutKey)
-> TimeoutCallback -> IO TimeoutKey
forall a b. (a -> b) -> a -> b
$
                  Word64 -> TimeoutCallback
rearm (Word64 -> TimeoutCallback) -> Word64 -> TimeoutCallback
forall a b. (a -> b) -> a -> b
$ Word64
passed Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
us''
      IO TimeoutKey -> TimeoutCallback
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO TimeoutKey -> TimeoutCallback)
-> IO TimeoutKey -> TimeoutCallback
forall a b. (a -> b) -> a -> b
$ TimerManager -> Int -> TimeoutCallback -> IO TimeoutKey
registerTimeout TimerManager
tmm (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
us') (TimeoutCallback -> IO TimeoutKey)
-> TimeoutCallback -> IO TimeoutKey
forall a b. (a -> b) -> a -> b
$ Word64 -> TimeoutCallback
rearm Word64
us'
      TVar Bool -> IO (TVar Bool)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return TVar Bool
ttv