posix-timer-0.2.0.1: src/System/Posix/Clock.hsc
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
-- | POSIX clocks.
module System.Posix.Clock (
TimeSpec,
timeSpecSeconds,
timeSpecNanos,
mkTimeSpec,
timeSpecV,
timeSpecToInt64,
Clock(..),
monotonicClock,
realtimeClock,
processTimeClock,
threadTimeClock,
getProcessClock,
getClockResolution,
getClockTime,
setClockTime,
clockSleep,
clockSleepAbs,
) where
import Data.Int
import Data.Word
import Data.Ratio (numerator)
import Data.List (unfoldr)
import Control.Applicative ((<$>), (<*>))
import Control.Monad.Base
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (Storable(..))
import Foreign.Marshal.Alloc (alloca)
import Foreign.Marshal.Utils (with)
import Foreign.C.Types (CULong)
#if __GLASGOW_HASKELL__ >= 703
import Foreign.C.Types (CInt(..), CTime(..))
#else
import Foreign.C.Types (CInt, CTime)
#endif
import Foreign.C.Error (getErrno, eINTR, throwErrno, throwErrnoIfMinus1_)
#if __GLASGOW_HASKELL__ >= 703
import System.Posix.Types (CPid(..))
#endif
import System.Posix.Types (ProcessID)
#if __GLASGOW_HASKELL__ < 703
import Unsafe.Coerce (unsafeCoerce)
#endif
#include <time.h>
#include <HsBaseConfig.h>
#include <posix-timer.macros.h>
#let alignment t = "%lu", (unsigned long) offsetof (struct { char x__; t (y__); }, y__)
nsPerSecond ∷ Num α ⇒ α
nsPerSecond = 1000000000
{-# INLINE nsPerSecond #-}
minSecsInInt, minNsInInt ∷ Int
(minSecsInInt, minNsInInt) = (minBound ∷ Int) `divMod` nsPerSecond
maxSecsInInt, maxNsInInt ∷ Int
(maxSecsInInt, maxNsInInt) = (maxBound ∷ Int) `divMod` nsPerSecond
-- | Mirrors /struct timespec/.
data TimeSpec = TimeSpec { timeSpecSeconds ∷ CTime
, timeSpecNanos ∷ CULong
} deriving (Eq, Show)
-- | Create a 'TimeSpec' from amounts of seconds and nanoseconds.
mkTimeSpec ∷ CTime → CULong → TimeSpec
mkTimeSpec s ns | ns < nsPerSecond = TimeSpec s ns
| otherwise = TimeSpec (s + fromIntegral q) r
where (q, r) = ns `quotRem` nsPerSecond
-- | Convert a 'TimeSpec' to a pair of its components.
-- Useful as a view pattern.
timeSpecV ∷ TimeSpec → (CTime, CULong)
timeSpecV (TimeSpec s ns) = (s, ns)
{-# INLINE timeSpecV #-}
instance Ord TimeSpec where
(TimeSpec s1 ns1) `compare` (TimeSpec s2 ns2) =
case s1 `compare` s2 of
EQ → ns1 `compare` ns2
x → x
instance Bounded TimeSpec where
minBound = TimeSpec (fromIntegral (minBound ∷ #{itype time_t})) 0
maxBound = TimeSpec (fromIntegral (maxBound ∷ #{itype time_t}))
(nsPerSecond - 1)
instance Num TimeSpec where
(TimeSpec s1 ns1) * (TimeSpec s2 ns2) =
mkTimeSpec (s1 * s2 * nsPerSecond +
s1 * (fromIntegral ns2) + s2 * (fromIntegral ns1) +
(fromIntegral q)) $ fromIntegral r
where (q, r) = ((fromIntegral ns1 ∷ Word64) *
(fromIntegral ns2 ∷ Word64)) `quotRem` nsPerSecond
(TimeSpec s1 ns1) + (TimeSpec s2 ns2) = mkTimeSpec (s1 + s2) (ns1 + ns2)
(TimeSpec s1 ns1) - (TimeSpec s2 ns2) =
if ns1 < ns2 then TimeSpec (s1 - s2 - 1) (nsPerSecond - ns2 + ns1)
else TimeSpec (s1 - s2) (ns1 - ns2)
negate (TimeSpec s ns) = mkTimeSpec ((-s) - 1) (nsPerSecond - ns)
abs ts@(TimeSpec s _) = if s >= 0 then ts else negate ts
signum (TimeSpec s ns) | s < 0 = TimeSpec (-1) (nsPerSecond - 1)
| otherwise = TimeSpec 0 $ signum ns
fromInteger i = TimeSpec (fromInteger s) (fromInteger ns)
where (s, ns) = i `divMod` nsPerSecond
instance Real TimeSpec where
toRational (TimeSpec s ns) = toRational s * nsPerSecond + toRational ns
instance Enum TimeSpec where
succ (TimeSpec s ns) | ns == nsPerSecond - 1 = TimeSpec (succ s) 0
| otherwise = TimeSpec s (succ ns)
pred (TimeSpec s ns) | ns == 0 = TimeSpec (pred s) (nsPerSecond - 1)
| otherwise = TimeSpec s (ns - 1)
toEnum i = TimeSpec (fromIntegral s) (fromIntegral ns)
where (s, ns) = i `divMod` nsPerSecond
fromEnum (TimeSpec s ns) =
if s' < minSecs || (s' == minSecs && ns < minNs) ||
s' > maxSecs || (s' == maxSecs && ns > maxNs)
then error "TimeSpec.fromEnum"
else fromIntegral s' * nsPerSecond + fromIntegral ns
where s', minSecs, maxSecs ∷ #{itype time_t}
#if __GLASGOW_HASKELL__ >= 703
CTime s' = s
#else
s' = unsafeCoerce s
#endif
minSecs = fromIntegral minSecsInInt
maxSecs = fromIntegral maxSecsInInt
minNs, maxNs ∷ CULong
minNs = fromIntegral minNsInInt
maxNs = fromIntegral maxNsInInt
enumFrom x = enumFromTo x maxBound
enumFromThen x y = enumFromThenTo x y bound
where bound | y >= x = maxBound
| otherwise = minBound
enumFromTo x y
| y >= x = unfoldr (\z → if z == y then Nothing else Just (z, z + 1)) x
| otherwise = unfoldr (\z → if z == y then Nothing else Just (z, z - 1)) x
enumFromThenTo x n y
| d >= 0 =
if y < x
then []
else unfoldr (\z → if z > y then Nothing else Just (z, z + d)) x
| otherwise =
if y > x
then []
else unfoldr (\z → if z < y then Nothing else Just (z, z - d)) x
where d = n - x
instance Integral TimeSpec where
toInteger (TimeSpec s ns) = (numerator $ toRational s) * nsPerSecond +
toInteger ns
-- TODO: Make it faster when the arguments are small enough
quotRem tsN tsD = (fromInteger q, fromInteger r)
where (q, r) = quotRem (toInteger tsN) (toInteger tsD)
timeSpecToInt64 ∷ TimeSpec → Int64
timeSpecToInt64 (TimeSpec s ns) =
#if __GLASGOW_HASKELL__ >= 703
let CTime s' = s in
fromIntegral s' * nsPerSecond +
#else
# if HTYPE_TIME_T == Int64
(unsafeCoerce s ∷ Int64) * nsPerSecond +
# elif HTYPE_TIME_T == Int32
(fromIntegral (unsafeCoerce s ∷ Int32) ∷ Int64) * nsPerSecond +
# else
# error FIXME: timeSpecToInt64: unexpected HTYPE_TIME_T value
# endif
#endif
fromIntegral ns
{-# INLINE timeSpecToInt64 #-}
{-# RULES
"fromIntegral/TimeSpec->Int64" fromIntegral = timeSpecToInt64
"fromIntegral/TimeSpec->Word64" fromIntegral = fromIntegral . timeSpecToInt64
#-}
instance Storable TimeSpec where
alignment _ = #{alignment struct timespec}
sizeOf _ = #{size struct timespec}
peek p = TimeSpec <$> #{peek struct timespec, tv_sec} p
<*> #{peek struct timespec, tv_nsec} p
poke p (TimeSpec seconds nanoseconds) = do
#{poke struct timespec, tv_sec} p seconds
#{poke struct timespec, tv_nsec} p nanoseconds
-- | Mirrors /clockid_t/.
newtype Clock = Clock #{itype clockid_t} deriving (Eq, Ord, Show, Storable)
#{enum Clock, Clock
, monotonicClock = CLOCK_MONOTONIC
, realtimeClock = CLOCK_REALTIME
, processTimeClock = CLOCK_PROCESS_CPUTIME_ID
, threadTimeClock = CLOCK_THREAD_CPUTIME_ID
}
-- | Get the CPU-time clock of the given process.
-- See /clock_getcpuclockid(3)/.
getProcessClock ∷ MonadBase μ IO ⇒ ProcessID → μ Clock
getProcessClock pid =
liftBase $ alloca $ \p → do
throwErrnoIfMinus1_ "getProcClock" $ c_clock_getcpuclockid pid p
peek p
-- | Get the clock resolution. See /clock_getres(3)/.
getClockResolution ∷ MonadBase μ IO ⇒ Clock → μ TimeSpec
getClockResolution clock =
liftBase $ alloca $ \p → do
throwErrnoIfMinus1_ "getClockResolution" $ c_clock_getres clock p
peek p
-- | Get the clock time. See /clock_gettime(3)/.
getClockTime ∷ MonadBase μ IO ⇒ Clock → μ TimeSpec
getClockTime clock =
liftBase $ alloca $ \p → do
throwErrnoIfMinus1_ "getClockTime" $ c_clock_gettime clock p
peek p
-- | Set the clock time. See /clock_settime(3)/.
setClockTime ∷ MonadBase μ IO ⇒ Clock → TimeSpec → μ ()
setClockTime clock ts =
liftBase $ with ts $
throwErrnoIfMinus1_ "setClockTime" . c_clock_settime clock
-- | Sleep for the specified duration. When interrupted by a signal, returns
-- the amount of time left to sleep. See /clock_nanosleep(3)/.
clockSleep ∷ MonadBase μ IO ⇒ Clock → TimeSpec → μ TimeSpec
clockSleep clock ts =
liftBase $ with ts $ \pTs →
alloca $ \pLeft → do
result ← c_clock_nanosleep clock 0 pTs pLeft
if result == 0
then return 0
else do
errno ← getErrno
if errno == eINTR
then peek pLeft
else throwErrno "clockSleep"
-- | Sleep until the clock time reaches the specified value.
-- See /clock_nanosleep(3)/.
clockSleepAbs ∷ MonadBase μ IO ⇒ Clock → TimeSpec → μ ()
clockSleepAbs clock ts =
liftBase $ with ts $ \p →
throwErrnoIfMinus1_ "clockSleepAbs" $
c_clock_nanosleep clock #{const TIMER_ABSTIME} p nullPtr
foreign import ccall unsafe "clock_getcpuclockid"
c_clock_getcpuclockid ∷ ProcessID → Ptr Clock → IO CInt
foreign import ccall unsafe "clock_getres"
c_clock_getres ∷ Clock → Ptr TimeSpec → IO CInt
foreign import ccall unsafe "clock_gettime"
c_clock_gettime ∷ Clock → Ptr TimeSpec → IO CInt
foreign import ccall unsafe "clock_settime"
c_clock_settime ∷ Clock → Ptr TimeSpec → IO CInt
foreign import ccall "clock_nanosleep"
c_clock_nanosleep ∷ Clock → CInt → Ptr TimeSpec → Ptr TimeSpec → IO CInt