{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
module Control.Concurrent.Timeout (
timeout,
threadDelay
) where
import Data.Typeable
import Data.Timeout
import Data.Unique
import Control.Applicative
import Control.Monad.Base
import Control.Exception
import qualified Control.Concurrent as C
import qualified System.Timeout as C
data TimeoutException = TimeoutException Unique
deriving (Typeable, TimeoutException -> TimeoutException -> Bool
(TimeoutException -> TimeoutException -> Bool)
-> (TimeoutException -> TimeoutException -> Bool)
-> Eq TimeoutException
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TimeoutException -> TimeoutException -> Bool
== :: TimeoutException -> TimeoutException -> Bool
$c/= :: TimeoutException -> TimeoutException -> Bool
/= :: TimeoutException -> TimeoutException -> Bool
Eq)
instance Show TimeoutException where
show :: TimeoutException -> String
show TimeoutException
_ = String
"<<timeout>>"
instance Exception TimeoutException
timeout ∷ MonadBase IO μ ⇒ Timeout → IO α → μ (Maybe α)
timeout :: forall (μ :: * -> *) α.
MonadBase IO μ =>
Timeout -> IO α -> μ (Maybe α)
timeout Timeout
tt IO α
_ | Timeout
tt Timeout -> Timeout -> Bool
forall a. Eq a => a -> a -> Bool
== Timeout
instantly = Maybe α -> μ (Maybe α)
forall a. a -> μ a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe α
forall a. Maybe a
Nothing
timeout Timeout
tt IO α
io = IO (Maybe α) -> μ (Maybe α)
forall α. IO α -> μ α
forall (b :: * -> *) (m :: * -> *) α. MonadBase b m => b α -> m α
liftBase (IO (Maybe α) -> μ (Maybe α)) -> IO (Maybe α) -> μ (Maybe α)
forall a b. (a -> b) -> a -> b
$ do
ThreadId
pid <- IO ThreadId
C.myThreadId
TimeoutException
ex <- Unique -> TimeoutException
TimeoutException (Unique -> TimeoutException) -> IO Unique -> IO TimeoutException
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Unique
newUnique
(TimeoutException -> Maybe ())
-> (() -> IO (Maybe α)) -> IO (Maybe α) -> IO (Maybe α)
forall e b a.
Exception e =>
(e -> Maybe b) -> (b -> IO a) -> IO a -> IO a
handleJust (\TimeoutException
e -> if TimeoutException
e TimeoutException -> TimeoutException -> Bool
forall a. Eq a => a -> a -> Bool
== TimeoutException
ex then () -> Maybe ()
forall a. a -> Maybe a
Just () else Maybe ()
forall a. Maybe a
Nothing)
(\()
_ -> Maybe α -> IO (Maybe α)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe α
forall a. Maybe a
Nothing)
(IO ThreadId
-> (ThreadId -> IO ())
-> (ThreadId -> IO (Maybe α))
-> IO (Maybe α)
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (IO () -> IO ThreadId
C.forkIO (Timeout -> IO ()
forall (μ :: * -> *). MonadBase IO μ => Timeout -> μ ()
threadDelay Timeout
tt IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ThreadId -> TimeoutException -> IO ()
forall e. Exception e => ThreadId -> e -> IO ()
C.throwTo ThreadId
pid TimeoutException
ex))
(ThreadId -> IO ()
C.killThread)
(\ThreadId
_ -> α -> Maybe α
forall a. a -> Maybe a
Just (α -> Maybe α) -> IO α -> IO (Maybe α)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO α
io))
threadDelay ∷ MonadBase IO μ ⇒ Timeout → μ ()
threadDelay :: forall (μ :: * -> *). MonadBase IO μ => Timeout -> μ ()
threadDelay Timeout
tt | Timeout
tt Timeout -> Timeout -> Bool
forall a. Eq a => a -> a -> Bool
== Timeout
instantly = () -> μ ()
forall a. a -> μ a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
threadDelay Timeout
tt = IO () -> μ ()
forall α. IO α -> μ α
forall (b :: * -> *) (m :: * -> *) α. MonadBase b m => b α -> m α
liftBase (IO () -> μ ()) -> IO () -> μ ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
C.threadDelay (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
us') IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Word64 -> IO ()
go Word64
us'
where 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
go :: Word64 -> IO ()
go Word64
passed = case Word64
us Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
passed of
Word64
0 → () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Word64
left → Int -> IO ()
C.threadDelay (Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
us'') IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Word64 -> IO ()
go Word64
us''
where us'' :: Word64
us'' = Word64
maxUs Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
`min` Word64
left