[Haskell-cafe] ANNOUNCE: system-time-monotonic-0.1

Joey Adams joeyadams3.14159 at gmail.com
Mon Aug 6 22:14:22 CEST 2012


system-time-monotonic [1] provides access to the system's monotonic
clock.  Usage looks like this:

 * Use 'newClock' to create a new monotonic clock

 * Use 'clockGetTime' to see how much time has elapsed since the clock
was created.

This package currently supports Linux and Windows.  It might also work
on BSD, but I haven't tested it there.

Mac OS X support is currently not implemented, but patches are
welcome.  GHC uses mach_absolute_time and mach_timebase_info; see
ticket #5865 [2].

I also added a handy utility function 'delay', a variant of
threadDelay taking a DiffTime instead of Int microseconds.  Thus:

    delay 1.5

Waits 1.5 seconds.  Note that since 'delay' simply calls 'threadDelay'
in a loop, it is disrupted by system clock changes (again, see ticket
#5865).

 [1]: http://hackage.haskell.org/package/system-time-monotonic
 [2]: http://hackage.haskell.org/trac/ghc/ticket/5865

---

The rest of this email describes various hurdles involved in
implementing this package, and how they were addressed.

## GetTickCount

The most obvious one is that GetTickCount has a short wraparound (49.7
days).  Two ways to address this:

 * Don't use GetTickCount; use QueryPerformanceCounter (or similar)
instead.  This is currently how it's done in GHC HEAD.

 * Use GetTickCount, but avoid comparing times that are far apart by
tracking the total difference each time we look at the clock.

I took the second approach, because I found out that
QueryPerformanceCounter is actually less accurate in the long run than
GetTickCount.  In particular, QueryPerformanceCounter stops ticking
(or maybe ticks slower, I forget) when the computer is asleep.

Here's the trick I use to work around GetTickCount's wraparound (pseudocode):

    st1 :: Word32
    t1  :: DiffTime

    newClock:
        st1 := GetTickCount()
        t1  := 0

    clockGetTime:
        st2 := GetTickCount()
        t2  := t1 + (Int32)(st2 - st1 modulo 2^32)

        st1 := st2
        t1  := t2

        return t2

This workaround only works if clockGetTime is called at least once
every 24.8 days.

It's important that st2 - st1 be done modulo 2^32, to compensate for
wraparound.  However, the result should be converted to a signed
32-bit integer, in case st2 was recorded earlier than st1 (which can
easily happen in a concurrent context).

In particular, here's what you should *not* do (which GHC currently
does, when QueryPerformanceCounter is not available):

    st1 = (bigger_type) GetTickCount();
    ...
    st2 = (bigger_type) GetTickCount();
    return (st2 - st1)

This will return a bogus value if a wraparound occurred between st1 and st2.

system-time-monotonic tests, at runtime, if GetTickCount64 is
available.  If so, it uses it.  Otherwise, it falls back to
GetTickCount.  Here's the code I used to do the run-time system call
lookup:

    /* cbits/dll.c */
    typedef ULONGLONG (WINAPI *GetTickCount64_t)(void);

    GetTickCount64_t system_time_monotonic_load_GetTickCount64(void)
    {
        return (GetTickCount64_t)
            GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
                           "GetTickCount64");
    }


    -- System/Time/Monotonic/Direct.hsc
    type C_GetTickCount64 = IO #{type ULONGLONG}

    foreign import ccall
        system_time_monotonic_load_GetTickCount64 :: IO (FunPtr
C_GetTickCount64)

    foreign import stdcall "dynamic"
        mkGetTickCount64 :: FunPtr C_GetTickCount64 -> C_GetTickCount64

Did I do this right?  In particular:

 * Can I assume the kernel32.dll HMODULE won't be pulled out from under me?

 * Is `foreign import stdcall "dynamic"` the right incantation for
using a pointer to a WINAPI function?

## CLOCK_MONOTONIC is not actually monotonic on Linux

CLOCK_MONOTONIC is subject to NTP adjustments.  Worse, CLOCK_MONOTONIC
stops when the computer is put to sleep (unlike GetTickCount, which
does the right thing).

Two clocks were introduced very recently in Linux:

 * CLOCK_MONOTONIC_RAW

 * CLOCK_BOOTTIME

I'd like to support CLOCK_BOOTTIME at some point.  I'm not sure if
it's subject to NTP adjustments or not, since the announcement [3]
says:

    CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except it also
    includes any time spent in suspend (as currently measured by
    read_persistent_clock()). This allows applications to get a
    suspend aware monotonic clock.

One idea would be to hard-code the clockid_t of CLOCK_BOOTTIME and
CLOCK_MONOTONIC_RAW, and try calling clock_gettime with each of these.
 If one of these succeeds, use it.  Otherwise, fall back to
CLOCK_MONOTONIC.  Thus, the compiled binary can test, at runtime, if a
new enough kernel is available.

For now, system-time-monotonic simply uses CLOCK_MONOTONIC.

 [3]: http://lwn.net/Articles/428176/



More information about the Haskell-Cafe mailing list