Skip to content

Commit 25163b8

Browse files
committed
IOCP.FFI: Add bindings to GetTickCount and QueryPerformanceCounter
1 parent 0092949 commit 25163b8

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

IOCP/FFI.hsc

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ module IOCP.FFI (
1717
-- * Cancel pending I/O
1818
cancelIo,
1919

20+
-- * Monotonic time
21+
-- | Windows has multiple monotonic time APIs, each with its own caveats.
22+
-- It seems that QueryPerformanceCounter has higher precision,
23+
-- while GetTickCount(64) has better accuracy and consistency.
24+
25+
-- ** GetTickCount
26+
getTickCount,
27+
loadGetTickCount64,
28+
29+
-- ** QueryPerformanceCounter
30+
queryPerformanceCounter,
31+
queryPerformanceFrequency,
32+
2033
-- * Miscellaneous
2134
throwWinErr,
2235
) where
@@ -142,6 +155,101 @@ foreign import WINDOWS_CCONV safe "windows.h CancelIo"
142155
cancelIo :: HANDLE -> IO ()
143156
cancelIo = Win32.failIfFalse_ "CancelIo" . c_CancelIo
144157

158+
------------------------------------------------------------------------
159+
-- Monotonic time
160+
161+
foreign import WINDOWS_CCONV "windows.h GetTickCount"
162+
c_GetTickCount :: IO #{type DWORD}
163+
164+
-- | Call the @GetTickCount@ function, which returns a monotonic time in
165+
-- milliseconds.
166+
--
167+
-- Problems:
168+
--
169+
-- * Low resolution (10 to 16 milliseconds).
170+
--
171+
-- * Wraps around when the system runs continuously for 49.7 days.
172+
--
173+
-- * Not available for Windows Store apps.
174+
--
175+
-- <http://msdn.microsoft.com/en-us/library/windows/desktop/ms724408%28v=vs.85%29.aspx>
176+
getTickCount :: IO Word32
177+
getTickCount = c_GetTickCount
178+
179+
type C_GetTickCount64 = IO #{type ULONGLONG}
180+
181+
-- Defined in cbits/dll.c
182+
foreign import ccall
183+
iocp_load_GetTickCount64 :: IO (FunPtr C_GetTickCount64)
184+
185+
foreign import WINDOWS_CCONV "dynamic"
186+
mkGetTickCount64 :: FunPtr C_GetTickCount64 -> C_GetTickCount64
187+
188+
-- | Load the @GetTickCount64@ function, or return 'Nothing' if it is
189+
-- not available.
190+
--
191+
-- Problems:
192+
--
193+
-- * Low resolution (10 to 16 milliseconds).
194+
--
195+
-- * Introduced in Windows Vista, so not available under Windows XP.
196+
--
197+
-- <http://msdn.microsoft.com/en-us/library/windows/desktop/ms724411%28v=vs.85%29.aspx>
198+
loadGetTickCount64 :: IO (Maybe (IO Word64))
199+
loadGetTickCount64 = do
200+
fun <- iocp_load_GetTickCount64
201+
if fun == nullFunPtr then
202+
return Nothing
203+
else
204+
return $ Just $ mkGetTickCount64 fun
205+
206+
type QPFunc = Ptr Int64 -> IO BOOL
207+
208+
foreign import WINDOWS_CCONV "Windows.h QueryPerformanceCounter"
209+
c_QueryPerformanceCounter :: QPFunc
210+
211+
foreign import WINDOWS_CCONV "Windows.h QueryPerformanceFrequency"
212+
c_QueryPerformanceFrequency :: QPFunc
213+
214+
callQP :: QPFunc -> IO (Maybe Int64)
215+
callQP qpfunc =
216+
allocaBytes #{size LARGE_INTEGER} $ \ptr -> do
217+
ok <- qpfunc ptr
218+
if ok then do
219+
n <- #{peek LARGE_INTEGER, QuadPart} ptr
220+
return (Just n)
221+
else
222+
return Nothing
223+
224+
-- | Call the @QueryPerformanceCounter@ function.
225+
--
226+
-- Problems:
227+
--
228+
-- * On a multiprocessor computer, may produce different results on
229+
-- different processors due to hardware bugs.
230+
--
231+
-- * May drift when the computer is put to sleep.
232+
--
233+
-- To get a monotonic time in seconds, divide the result of
234+
-- 'queryPerformanceCounter' by that of 'queryPerformanceFrequency'.
235+
--
236+
-- <http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904%28v=vs.85%29.aspx>
237+
queryPerformanceCounter :: IO (Maybe Int64)
238+
queryPerformanceCounter = callQP c_QueryPerformanceCounter
239+
240+
-- | Call the @QueryPerformanceFrequency@ function. Return 'Nothing' if the
241+
-- hardware does not provide a high-resolution performance counter.
242+
--
243+
-- <http://msdn.microsoft.com/en-us/library/windows/desktop/ms644905%28v=vs.85%29.aspx>
244+
queryPerformanceFrequency :: IO (Maybe Int64)
245+
queryPerformanceFrequency = do
246+
m <- callQP c_QueryPerformanceFrequency
247+
case m of
248+
Nothing -> return Nothing
249+
Just 0 -> return Nothing -- Shouldn't happen; just a safeguard to
250+
-- avoid a zero denominator.
251+
Just freq -> return (Just freq)
252+
145253
------------------------------------------------------------------------
146254
-- Miscellaneous
147255

cbits/dynamic.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <windows.h>
2+
3+
typedef ULONGLONG (WINAPI *GetTickCount64_t)(void);
4+
5+
GetTickCount64_t iocp_load_GetTickCount64(void)
6+
{
7+
return (GetTickCount64_t)
8+
GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
9+
"GetTickCount64");
10+
}

iocp.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ library
2727
Winsock
2828

2929
c-sources:
30+
cbits/dynamic.c
3031
cbits/iocp.c
3132
cbits/Winsock.c
3233

0 commit comments

Comments
 (0)